IOC概述
IOC(inversion of control)的中文解释是“控制反转”,对象的使用者不是创建者. 作用是将对象的创建 反转给spring框架来创建和管理。
控制反转怎么去理解呢。 其实它反转的是什么呢,是对象的创建工作。 举个例子:平常我们在servlet或者service里面创建对象,都是使用new 的方式来直接创建对象,现在有了spring之后,我们就再也不new对象了,而是把对象创建的工作交给spring容器去维护。我们只需要问spring容器要对象即可
ioc 的作用:削减计算机程序的耦合(解除我们代码中的依赖关系)。
分析和IOC的引入
上一小节我们通过使用工厂模式,实现了表现层——业务层以及业务层——持久层的解耦。
它的核心思想就是:
1、通过读取配置文件反射创建对象。
2、把创建出来的对象都存起来,当我们下次使用时可以直接从存储的位置获取。
这里面要解释两个问题:
第一个:存哪去?
分析:由于我们是很多对象,肯定要找个集合来存。这时候有 Map 和 List 供选择。
到底选 Map 还是 List 就看我们有没有查找需求。有查找需求,选 Map。
所以我们的答案就是:在应用加载时,创建一个 Map,用于存放三层对象。我们把这个 map 称之为容器。
第二个: 什么是工厂?
工厂就是负责给我们从容器中获取指定对象的类。这时候我们获取对象的方式发生了改变。
原来:我们在获取对象时,都是采用 new 的方式。 是主动的。
- 老方式
- 现在 我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。 是被动的。
小结
- IOC概念: 控制反转. 说白了: 就是把对象交给Spring框架去创建和管理. 我们要用对象, 直接从Spring的IOC容器【集合】里面获得
- 好处: 解耦, 到时候我们只需要修改配置文件, 不需要修改源码
知识点-工厂模式解耦
在进行软件设计时,应力争做到高内聚,低耦合。
什么是高内聚,低耦合?
就好比如台上的杂技表演,表演者在台上相互配合,少了谁也完成不了表演,而表演者在台下是互不打扰的,比如张三少了李四不可能就活不下去把?
在代码中体现
早期我们的 JDBC 操作,注册驱动时,我们为什么不使用 DriverManager 的 register 方法,而是采
用 Class.forName 的方式?
原因就是: 我们的类依赖了数据库的具体驱动类(MySQL) ,如果这时候更换了数据库(比如 Oracle) ,
需要修改源码来重新数据库驱动。这显然不是我们想要的。
/**
* 程序的耦合
* 耦合:程序间的依赖关系
* 包括:
* 类之间的依赖
* 方法间的依赖
* 解耦:
* 降低程序间的依赖关系
* 实际开发中:
* 应该做到:编译期不依赖,运行时才依赖。
* 解耦的思路:
* 第一步:使用反射来创建对象,而避免使用 new 关键字。
* 第二步:通过读取配置文件来获取要创建的对象全限定类名
*/
public class JdbcDemo1 {
public static void main(String[] args) throws Exception{
//1.注册驱动
//DriverManager.registerDriver(new Driver());
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
//3.获取操作数据库的预处理对象
//4.执行 SQL,得到结果集
//5.遍历结果集
//6.释放资源
}
}
解决程序耦合的思路
当是我们讲解 jdbc 时,是通过反射来注册驱动的,代码如下:
Class.forName(“com.mysql.jdbc.Driver”);//此处只是一个字符串
此时的好处是,我们的类中不再依赖具体的驱动类,此时就算删除 mysql 的驱动 jar 包,依然可以编译(运行就不要想了没有驱动不可能运行成功的) 。
同时,也产生了一个新的问题, mysql 驱动的全限定类名字符串是在 java 类中写死的,一旦要改还是要修改源码。解决这个问题也很简单,使用配置文件配置
工厂模式解耦
工厂模式解耦思路
在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候, 让一个类中的方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。那么,这个读取配置文件, 创建和获取三层对象的类就是工厂
实现
-
添加坐标依赖
<dependencies> <!-- 解析 xml 的 dom4j --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <!-- dom4j 的依赖包 jaxen --> <dependency> <groupId>jaxen</groupId> <artifactId>jaxen</artifactId> <version>1.1.6</version> </dependency> <!--单元测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> </dependencies>
-
AccountDao .java
public interface AccountDao { /** * 保存账户 */ void save(); }
AccountDaoImpl.java
public class AccountDaoImpl implements AccountDao {
/**
* 保存账户
*/
@Override
public void save() {
System.out.println("AccountDaoImpl... save()");
}
}
-
AccountService.java
public interface AccountService { /** * 保存账户 */ void save(); }
AccountServiceImpl.java
public class AccountServiceImpl implements AccountService {private AccountDao accountDao = (AccountDao) BeanFactory.getBean("accountDao"); /** * 保存账户 */ @Override public void save() { System.out.println("AccountServiceImpl---saveAccount()"); accountDao.save(); }
}
-
Client.java
public class Client { public static void main(String[] args) { AccountService accountService = (AccountService) BeanFactory.getBean("accountService"); accountService.save(); }
}
-
BeanFactory.java
下面的通过 BeanFactory 中 getBean 方法获取对象就解决了我们代码中对具体实现类的依赖。public class BeanFactory { //定义map private static Map<String, Object> beans; static{ try { beans = new HashMap<String, Object>(); //解析xml, 初始化beans集合 //1. 创建SaxReader对象 SAXReader saxReader = new SAXReader(); //2. 读取配置文件 获得document对象 Document document = saxReader.read(BeanFactory.class.getClassLoader().getResourceAsStream("applicationContext.xml")); //3. 获得所有的bean标签对象List List<Element> beanList = document.selectNodes("//bean"); //4. 遍历 for (Element element : beanList) { //获得id的属性值作为map的key String id = element.attributeValue("id"); //获得class的属性值,反射得到对象作为map的value String className = element.attributeValue("class"); beans.put(id, Class.forName(className).newInstance()); } } catch (Exception e) { e.printStackTrace(); } } public static Object getBean(String id){ return beans.get(id); }
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<!--id就是接口的名字, class实现类的全限定名 -->
<bean id="userDao" class="com.lakes.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.lakes.service.impl.UserServiceImpl"></bean>
</beans>
小结
- 先把配置文件的所有的bean标签解析出来
- 创建对象
- 把id作为key, 把对象作为value 存到容器(Map)
对象不需要我们创建了, 直接从容器里面取