控制反转IOC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现Ioc 的一种方法。没有Ioc的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制。控制反转后将对象的创建转移给第三方(交给Spring容器来创建)!!灵活性更高。
也可以这么理解,早期用户调用的功能是程序员指定的,但是用了控制反转去实现之后,控制权交给用户后,程序员写好功能,用户想调用哪个功能就调用哪个!
作用:降低耦合度(也就是降低模块之间的依赖)
下面会通过举例子说明控制反转带来的好处。
(1)创建UserDao接口
public interface UserDao {
void save();
}
(2)创建UserDaoImpl实现UserDao
public class userDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("保存用户数据");
}
}
(3)创建业务层UserService接口
public interface UserService {
void save();
}
(4)创建业务层UserServiceImpl实现UserService(由程序来控制创建对象)
public class UserServiceImpl implements UserService {
//最原始的方法,由程序来控制创建对象
private UserDao userDao=new userDaoImpl();
@Override
public void save() {
userDao.save();
}
}
(5)测试
@Test
public void test11(){
UserService userService=new UserServiceImpl();//用户实际调用的是业务层
userService.save();
}
当用户需求改变,要保存mysql 的数据,那么就需要改变业务方法,这里改变daoImpl的内容即可
public class userDaoMysqlImpl implements UserDao {
@Override
public void save() {
System.out.println("保存Mysql");
}
}
那么这时候我们就需要去ServiceImpl层里面修改private UserDao userDao=new userDaoImpl();为下面代码,也就是程序员需要去改变源代码,那么当用户的需求不断改变时,也意味着源码需要不断的改变,所以这就是耦合度高的缺点!!
//private UserDao userDao=new userDaoImpl();
private UserDao userDao=new userDaoMysqlImpl();
(1)新建静态工厂负责创建UserDao
public class StaticFactory {
public static UserDao getUserDao(){
return new userDaoImpl();
}
}
(2)ServiceImpl改变成:
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Override
public void save() {
userDao = StaticFactory.getUserDao();//工厂模式创建对象
userDao.save();
}
}
(3)其他的dao和service和上面一样,那么如果用户需求像上面一样改变,那么源代码就不需要变动了,但是!!需要改变工厂类的方法。比如用户需求新增保存mysql数据,那么工厂类应该变为:
public class StaticFactory {
public static UserDao getUserDao(){
return new userDaoMysqlImpl();
}
}
(4)测试
@Test
public void test11(){
UserService userService=new UserServiceImpl();//用户实际调用的是业务层
userService.save();
}
通过工厂模式解耦,实则是把Service层和dao层的耦合变成Service-工厂-dao层的耦合,当需求改变,源代码不变,但是工厂类需要变,所以依然还是存在着比较高的耦合度
思想:利用set方法实现动态值的注入
(1)UserDaoSeriviceImpl改为:
public class UserServiceImpl implements UserService {
private UserDao userDao;
//set方法实现动态注入对象
public void setUserDao(UserDao userDao){
this.userDao=userDao;
}
@Override
public void save() {
userDao.save();
}
}
(2)测试(只需要用户动态传入对象即可,源代码不需要改变)
下面的代码之所以需要强转类型是因为UserService没有setUserDao这个方法,所以需要强转为实现类调用。而用接口变量指向实现类对象是符合多态思想的。
@Test
public void test11(){
UserService userService=new UserServiceImpl();//用户实际调用的是业务层
((UserServiceImpl) userService).setUserDao(new userDaoImpl());
userService.save();
}
优点:之前我们都是程序主动创建对象(private UserDao userDao=new userDaoImpl())!控制权在程序员手上。但是现在使用set方法,程序不再具有主动性,而是被动的接受对象!!这也就是控制反转(控制权的反转)。这也是IOC的原型。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从loc容器中取出需要的对象。控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,Dl)。
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。
反转:程序本身不创建对象,而变成被动的接收对象。
(1)首先我们先了解
id属性:在容器中Bean实例的唯一标识,不允许重复
class属性:要实例化的Bean的全限定名
scope属性:Bean 的作用范围,常用是Singleton(默认)和prototype
(2)
name属性:属性名称
value:注入的普通属性值
ref属性:注入的对象引用值
(3)
Beans类必须提供有参构造方法
(1)构造方法下标赋值
(2)构造方法参数类型赋值(不建议使用)
(3)直接通过构造方法参数名设置(推荐)
Bean类必须有一个无参构造方法,Beans类必须为属性提供setter方法
(1)普通版本,举个栗子:为service注入dao
(2)P命名空间注入,本质也是set方法注入,但比起上述的se方法注入更加方便,主要体现在配置文件中,如下:
首先,需要引入P命名空间:
xmlna:p="http://www.apxingframework.org/achema/p"
其次,需要修改注入方式
OK,使用xml配置法实现Bean的依赖注入之后,一旦有需求变化,我们彻底不需要去程序中更改代码,只需要在xml配置文件中进行修改。
@Test
public void test11(){
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");//只要一读完配置文件马上就创建对象
Dog dog = (Dog) app.getBean("dog");
Dog dog1 = (Dog) app.getBean("dog");
System.out.println(dog==dog1);//运行结果是true,也就是说Spring容器同一个对象只存放一个实例
}
这里的ApplicationContext是Spring的一个容器,这里顺带讲一下Spring容器的使用
上面的操作,都是注入的引用Bean,处了对象的引用可以注入,普通数据类型,集合等都可以在容器中进行注入。
注入数据的三种数据类型
水浒传
红楼梦
这里的name指的是set方法去掉set之后,第一个字母小写的属性
这里的list在userdao里面是这样定义的:
public void setStrList(List strList) {
this.strList = strList;
}
aaa
bbb
ccc
这里的map在userdao里面是这样定义的:
public void setUsermap(Map usermap) {
this.usermap = usermap;
}
这里的properties在userdao里面是这样定义的:
public void setProperties(Properties properties) {
this.properties = properties;
}
ppp1
ppp2
ppp3
1、单例模式(Spring默认机制),内存中只有一份实例!!
2、原型模式:每次从容器中get的时候,都会产生一个新对象!!
3、默认为单例模式的时候测试
@Test
public void test11(){
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");//只要一读完配置文件马上就创建对象
Dog dog = (Dog) app.getBean("dog");
Dog dog1 = (Dog) app.getBean("dog");
System.out.println(dog==dog1);//运行结果是true,也就是说Spring容器同一个对象只存放一个实例
}
实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载。(resource等于其他配置文件的名称)
.