IOC理论推导
1、没有Spring前,代码是怎么来完成的?
要写dao层的接口和实现类,再写service层的接口和实现类
- UserDao 接口
- UserDaoImpl 实现类
- UserService 业务接口
- UserServiceImpl 业务实现类
-- 之前,业务层要自己去创建dao层对象,才能调到新增的dao层的方法
举例:提供获取用户数据的功能
1)先写一个UserDao接口:com.nikey.dao.UserDao
package com.nikey.dao;
public interface UserDao {
void getUser();
}
2)再去写Dao的实现类:com.nikey.dao.UserDaoImpl
package com.nikey.dao;
public class UserDaoImpl implements UserDao {
@Override
public void getUser() {
System.out.println("默认获取用户的数据");
}
}
3)然后去写UserService的接口:com.nikey.service.UserService
package com.nikey.service;
public interface UserService {
void getUser();
}
4)最后写Service的实现类:com.nikey.service.UserServiceImpl
package com.nikey.service;
import com.nikey.dao.UserDao;
import com.nikey.dao.UserDaoImpl;
public class UserServiceImpl implements UserService {
// 业务层要自己创建dao层对象
private UserDao userDao = new UserDaoImpl();
@Override
// 业务层调dao层去查用户
public void getUser() {
userDao.getUser();
}
}
5、测试一下:创建一个测试方法MyTest
import com.nikey.dao.UserDaoImpl;
import com.nikey.service.UserServiceImpl;
public class MyTest {
public static void main(String[] args) {
// 用户实际调用的是业务层,dao层他们不需要接触
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDao(new UserDaoImpl());
userService.getUser();
System.out.println("-------------------");
}
}
测试结果:service层调用dao层的方法,dao层模拟获取数据库中的数据

现在需求有变化了,要变成使用mysql数据库来获取用户数据,需要修改以下文件:
把Userdao的实现类增加一个,新增mysql的dao层实现类:com.nikey.dao.UserDaoMysqlImpl
package com.nikey.dao;
//增加一个mysql获取用户数据的实现
public class UserDaoMysqlImpl implements UserDao {
@Override
public void getUser() {
System.out.println("Mysql获取用户数据");
}
}
去service实现类里面修改对应的实现(修改为创建UserDaoMysqlImpl的对象):com.nikey.service.UserServiceImpl
UserServiceImpl修改后:private UserDao userDao = new UserDaoMysqlImpl();
UserServiceImpl修改前:private UserDao userDao = new UserDaoImpl();
package com.nikey.service;
import com.nikey.dao.UserDao;
import com.nikey.dao.UserDaoMysqlImpl;
public class UserServiceImpl implements UserService {
// 修改为创建需要的类型的dao层实现类(如mysql,oracle,nosql等)
private UserDao userDao = new UserDaoMysqlImpl();
@Override
// 业务层调dao层去查用户
public void getUser() {
userDao.getUser();
}
}
运行测试方法,测试结果:(底层获取用户数据的数据源有变化了,service层实现类要修改,测试方法不需要修改)

如果需求又变化了,要使用oracle来实现,那么修改如下内容:
1)新增oracle的dao层的实现类
2)修改service层的实现类,去创建UserDaoOracleImpl的对象
这就是service层实现类,自己主动创建对象的弊端了,如果dao层实现类有变化,每次都要修改service层的实现类;
每次变动 , 都需要修改大量代码 . 这种设计的耦合性太高了, 牵一发而动全身 .
-- 现在如何去解决之前的问题呢 ?
要解决这个问题,从客户端去调用需要的实现类(默认的,mysql,oracle或nosql的),service层不需要每次都修改,可以在service层实现类中增加一个方法:public void setUserDao(UserDao userDao)
利用set进行动态实现值的注入,而不是每次主动new UserDaoMysqlImpl()
com.nikey.service.UserServiceImpl
package com.nikey.service;
import com.nikey.dao.UserDao;
import com.nikey.dao.UserDaoMysqlImpl;
public class UserServiceImpl implements UserService {
// 修改为创建需要的类型的dao层实现类(如mysql,oracle,nosql等)
// private UserDao userDao = new UserDaoMysqlImpl();
private UserDao userDao;
// 利用set进行动态实现值的注入,而不是每次主动new UserDaoMysqlImpl()
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
@Override
// 业务层调dao层去查用户
public void getUser() {
userDao.getUser();
}
}
测试方法修改: userService.setUserDao(new UserDaoMysqlImpl());
让客户端去选择,要使用new UserDaoImpl(),还是new UserDaoMysqlImpl()
import com.nikey.dao.UserDaoImpl;
import com.nikey.dao.UserDaoMysqlImpl;
import com.nikey.service.UserServiceImpl;
public class MyTest {
public static void main(String[] args) {
// 用户实际调用的是业务层,dao层他们不需要接触
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDao(new UserDaoImpl());
userService.getUser();
System.out.println("-------------------");
userService.setUserDao(new UserDaoMysqlImpl());
userService.getUser();
}
}
在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改原代码,如果程序代码量十分大,修改一次的成本代价十分昂贵!
- 之前,程序是主动创建对象,控制权在程序员手上;(用户的每一个需求,都要修改代码)
- 使用了set注入后,程序不再具有主动性,而是变成了被动接受对象,把主动权交给了调用者 ;
这种思想,从本质上解决了问题,程序员不用再去管理怎么创建对象,怎么实现了,它只负责提供一个接口,系统的耦合性大大降低,可以更专注在业务实现上,这就是IOC原型;
之前:
逻辑代码在业务层,主动权在业务层,在程序员手上,不管增加多少dao层实现类,用户拿到的都是一样的;

以后:
主动权在用户,用户选择去调用哪里;

2、IOC本质
- 控制反转IOC(Inversion of Control),是一种设计思想;
- DI(依赖注入)是实现IOC的一种方法;
- 没有IOC的程序中,使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制;
- 控制反转后,将对象创建转移给第三方,即获得依赖对象的方式反转了;
图1中,对象A,B,C,D之间互相有关系,一层调一层
图2中,IOC容器连接对象A,B,C,D,使他们之间进行了解耦,不在有强耦合性
图3中,就实现了用户想要去调谁,就指定调谁就可以了

上层(service层)和下层(dao层)之间设计了一个接口(setUserDao(UserDao userDao)),通过接口去调用,就没有强联系性了。
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
- IOC是Spring框架的核心内容,使用多种方式完美的实现了IOC;
- 可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IOC;
- Spring容器在初始化时,先读取配置文件,根据配置文件或元数据,创建与组织对象存入容器中,程序使用时再从IOC容器中取出需要的对象;

- 采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的;
- 采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转,是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式;
在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
HelloSpring
导入Jar包
注 : spring 需要导入commons-logging进行日志记录.
1、编写一个Hello实体类
package com.nikey.pojo;
public class Hello {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}
2、编写spring文件 , 这里自己命名为beans.xml
Idea中:New-->XML Configuration File-->Spring Config-->bean.xml

类型 变量名 = new 类型();
Hello hello = new Hello();
- bean 等价于对象,相当于上面Hello hello = new Hello()过程;
- 其中,id等价于变量名hello,class等价于要 new的对象的类型(全路径名)
- property相当于给对象中的属性设置一个值;
- 通过这种方式,来new一个对象(交给容器去做了),只要配置就可以,不需要主动去创建对象了;
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--bean就是java对象 , 由Spring创建和管理-->
<bean id="hello" class="com.nikey.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
</beans>
3、进行测试
import com.nikey.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
//解析beans.xml文件,生成管理相应的Bean对象,配置文件可以传入多个,逗号分隔
//获取Spring上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//我们的对象现在都在Spring中管理了,要使用,直接去里面取出来就可以
//getBean : 参数即为spring配置文件中bean的id .
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}
测试结果:

4、思考问题:
- Hello 对象是谁创建的 ? ---hello 对象是由Spring创建的
- Hello 对象的属性是怎么设置的 ? ---hello 对象的属性是由Spring容器设置的
这个过程就叫控制反转 :
-
控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的
-
反转 : 程序本身不创建对象 , 而变成被动的接收对象 .
IOC是一种编程思想,由主动的编程变成被动的接收;
可以通过newClassPathXmlApplicationContext去浏览一下底层源码
依赖注入 : 就是利用set方法来进行注入的;
如果把Hello类中的setStr(String str)方法去掉,bean.xml就会报错,核心就是通过set方法,去完成注入设值的;
public void setStr(String str) {
this.str = str;
}

要实现不同的操作,只需要在xml配置文件中进行修改,所谓的IOC,即对象由Spring来创建,管理,装配。
修改案例一
将实现类的对象的创建,装配交给Spring来管理了;
在案例一中, 新增一个Spring配置文件bean-01-ioc1.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--bean就是java对象 , 由Spring创建和管理-->
<bean id="userDaoImpl" class="com.nikey.dao.impl.UserDaoImpl"/>
<bean id="userDaoMysqlImpl" class="com.nikey.dao.impl.UserDaoMysqlImpl"/>
<bean id="userDaoOracleImpl" class="com.nikey.dao.impl.UserDaoOracleImpl"/>
<bean id="userServiceImpl" class="com.nikey.service.impl.UserServiceImpl">
<!--注意: 这里的name并不是属性 , 而是set方法后面的那部分 , 首字母小写-->
<!--引用另外一个bean , 不是用value 而是用 ref-->
<!--ref:引用Spring容器中创建好的对象-->
<!--value: 具体的值,基本数据类型-->
<property name="userDao" ref="userDaoImpl"/>
</bean>
</beans>
测试:
import com.nikey.service.impl.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyIoc1Test {
public static void main(String[] args) {
// 获取ApplicationContext:拿到Spring 的容器
ApplicationContext context = new ClassPathXmlApplicationContext("bean-01-ioc1.xml");
// 需要什么对象,就getBean获取什么
UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("userServiceImpl");
// 需要获取什么类型的UserDao实现类,在bean.xml配置文件中修改property的ref
userServiceImpl.getUser();
}
}
如果需要修改UserDao的实现类为mysql或oracle的实现类,只需要修改Spring配置文件bean.xml中property的ref值即可;
测试类都不需要任何修改,再运行测试类,测试结果就改变了;
修改前:<property name="userDao" ref="userDaompl"/>
修改后:<property name="userDao" ref="userDaoOracleImpl"/>
<bean id="userServiceImpl" class="com.nikey.service.impl.UserServiceImpl">
<!--注意: 这里的name并不是属性 , 而是set方法后面的那部分 , 首字母小写-->
<!--引用另外一个bean , 不是用value 而是用 ref-->
<!--ref:引用Spring容器中创建好的对象-->
<!--value: 具体的值,基本数据类型-->
<property name="userDao" ref="userDaoOracleImpl"/>
</bean>
本文深入探讨了控制反转(IOC)的设计思想,及其在Spring框架中的应用。详细讲解了IOC如何通过依赖注入(DI)降低代码耦合度,提高程序灵活性。通过实例演示了Spring如何配置和管理Bean,实现对象的创建和依赖关系的动态注入。
1272

被折叠的 条评论
为什么被折叠?



