1. 问题分析
代码目录如下:
/**
* 账户持久层接口
*/
public interface IAccountDao {
/**
* 模拟保存账户
*/
void saveAccount();
}
/**
* 账户持久层实现类
*/
public class AccountDaoImpl implements IAccountDao {
public void saveAccount() {
System.out.println("保存账户 ");
}
}
/**
* 账户业务层的接口
*/
public interface IAccountService {
/**
* 模拟保存账户
*/
void saveAccount();
}
/**
* 账户业务层实现类
*/
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao = new AccountDaoImpl();
public void saveAccount() {
accountDao.saveAccount();
}
}
代码 private IAccountDao accountDao = new AccountDaoImpl();
耦合程度较高,代码独立性差。
2.使用工厂模式解耦
首先,我们需要一个创建bean对象的工厂(bean:在计算机英语中,有可重用组件的含义),
这个工厂需要:
-
需要一个配置文件来配置我们的service和dao
配置文件的内容:唯一标识=全限定类名 (key=value)
-
通过读取配置文件中配置的内容,反射创建对象
Class.forName
Class.forName
:返回与给定的字符串名称相关联类或接口的Class对象。
Class.forName
是一个静态方法,同样可以用来加载类。该方法有两种形式:
-
Class.forName(String name, boolean initialize, ClassLoader loader)
参数 name表示的是类的全名;initialize表示是否初始化类;loader表示加载时使用的类加载器 -
Class.forName(String className)
相当于设置了参数 initialize的值为 true,loader的值为当前类的类加载器。
public class BeanFactory {
//定义一个properties对象
private static Properties props;
//使用静态代码块为Properties对象赋值
static {
try {
props = new Properties();
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
}catch (Exception e){
throw new ExceptionInInitializerError("初始化properties失败");
}
}
/**
* 根据bean的名称获取bean对象
* @param beanName
* @return
*/
public static Object getBean(String beanName){
Object bean =null;
try {
String beanPath = props.getProperty(beanName);
bean = Class.forName(beanPath).newInstance();
}catch (Exception e){
e.printStackTrace();
}
return bean;
}
}
/**
* 账户业务层实现类
*/
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");
public void saveAccount() {
accountDao.saveAccount();
}
}
accountService = com.zcj.service.impl.AccountServiceImpl
accountDao = com.zcj.dao.impl.AccountDaoImpl
这样就可以减少代码的耦合程度,同时,在我们需要更改接口为其他接口版本时,就可以通过更改配置文件来更改接口版本。
3.容器
每调用BeanFactory.getBean
就会产生一个新的对象,我们需要记住对象中的属性是会在调用该函数后重新初始化的。当我们需要获取的对象是一个单例时,就需要使用容器来存储单例对象。
public class BeanFactory {
//定义一个properties对象
private static Properties props;
//定义一个map,用于存放我们要创建的对象,我们把它称之为容器
private static Map<String,Object> beans;
//使用静态代码块为Properties对象赋值
static {
try {
props = new Properties();
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
//实例化容器
beans = new HashMap<String, Object>();
//取出配置文件中的所有Key
Enumeration keys = props.keys();
while (keys.hasMoreElements()){
//取出每个key
String key = keys.nextElement().toString();
//根据key获取value
String beanPath = props.getProperty(key);
//反射创建对象
Object value = Class.forName(beanPath).newInstance();
//把key和value 存入容器中
beans.put(key,value);
}
}catch (Exception e){
throw new ExceptionInInitializerError("初始化properties失败");
}
}
/**
* 根据bean的名称获取bean对象
* @param beanName
* @return
*/
public static Object getBean(String beanName){
return beans.get(beanName);
}
}
/**
* 账户业务层实现类
*/
public class AccountServiceImpl implements IAccountService {
public void saveAccount() {
IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");
accountDao.saveAccount();
}
}
4.IoC(Inversion of Control)
当使用new创建对象时,是app主动的。
当使用工厂模式创建对象时,相当于把主动权从app中让出,交给factory,app断开与资源的联系,这种控制权的转移,称为控制反转,可以减低依赖关系,削减耦合程度。
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
spring能主动地帮我们做好IoC。