Spring IOC控制反转及DI注入 XML详解
一.spring IOC 原理详解
创建对象时,我们一直倡导低耦合,少侵入原则,于是应面向接口编程
[code]//原来的写法 public class BookServiceImpl { private BookDaoImpl bookDaoImpl; public void oldCode(){ bookDaoImpl=new bookDaoImpl(); bookDaoImpl.getAllCategories(); } } //面向接口 public class BookServiceImpl { //interface private BookDao bookDao; public void newCode(){ //变为面向接口编程 bookDao=new bookDaoImpl(); bookDao.getAllCategories(); } }
这样做的好处是,当需要改变实现类时,只需要将bookDao指向新的实现类
上述代码很大程度降低了耦合度,但是代码依旧存在入侵性和一定程度的耦合性,比如在修改bookDao的实现类时,仍然需求修改BookServiceImpl的内部代码,当依赖的类多起来时,查找和修改的过程也会显得相当糟糕
实际上存在一种称为反射的编程技术可以协助解决上述问题,反射是一种根据给出的完整类名(字符串方式)来动态地生成对象,这种编程方式可以让对象在生成时才决定到底是哪一种对象,因此可以这样假设,在某个配置文件,该文件已写好bookDaoImpl类的完全限定名称,通过读取该文件而获取到bookDao的真正实现类完全限定名称,然后通过反射技术在运行时动态生成该类,最终赋值给bookDao接口,也就解决了刚才的存在问题
使用properties文件作为配置文件
[code]bookDao.name=com.zejian.spring.dao.BookDaoImpl
[code]public class BookServiceImpl implements BookService { //读取配置文件的工具类 PropertiesUtil propertiesUtil=new PropertiesUtil("conf/className.properties"); private BookDao bookDao; public void DaymicObject() throws ClassNotFoundException, IllegalAccessException, InstantiationException { //获取完全限定名称 String className=propertiesUtil.get("bookDao.name"); //通过反射 Class c=Class.forName(className); //动态生成实例对象 bookDao= (BookDao) c.newInstance(); } }
这样做,要改变bookDao的实现类时,只需要修改配置文件中的具体实现类即可,大大降低了代码的耦合度
了解了上述的问题再来理解IOC就显得简单多了。Spring IOC 也是一个Java对象,在某些特定的时间被创建后,可以进行对其他对象的控制,包括初始化、创建、销毁等。
简单地理解,在上述过程中,我们通过配置文件配置了BookDaoImpl实现类的完全限定名称,然后利用反射在运行时为BookDao创建实际实现类,包括BookServiceImpl的创建,Spring的IOC容器都会帮我们完成,而我们唯一要做的就是把需要创建的类和其他类依赖的类以配置文件的方式告诉IOC容器需要创建那些类和注入哪些类即可。
Spring通过这种控制反转(IoC)的设计模式促进了松耦合,这种方式使一个对象依赖其它对象时会通过被动的方式传送进来(如BookServiceImpl被创建时,其依赖的BookDao的实现类也会同时被注入BookServiceImpl中),而不是通过手动创建这些类。我们可以把IoC模式看做是工厂模式的升华,可以把IoC看作是一个大工厂,只不过这个大工厂里要生成的对象都是在配置文件(XML)中给出定义的,然后利用Java的反射技术,根据XML中给出的类名生成相应的对象。
从某种程度上来说,IoC相当于把在工厂方法里通过硬编码创建对象的代码,改变为由XML文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性,更是达到最低的耦合度,因此我们要明白所谓为的IOC就将对象的创建权,交由Spring完成,从此解放手动创建对象的过程,同时让类与类间的关系到达最低耦合度。
二.第一个spring IOC入门程序
1.创建一个Maven项目,依赖spring IOC所需要的包
[code]<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency>
2.创建DAO层
HaohanDao接口
[code]public interface HaohanDao { void add(); }
HaohanImpl实现类
[code]public class HaohanImpl implements HaohanDao { @Override public void add() { System.out.println("添加了好汉..."); } }
3.创建service层
HaohanService接口
[code]public interface HaohanService { void doSomething(); }
HanhanServiceImpl实现类
[code]public class HaohanServiceImpl implements HaohanService { private HaohanDao haohanDao; public void setHaohanDao(HaohanDao haohanDao) { this.haohanDao = haohanDao; } @Override public void doSomething() { System.out.println("HaohanServiceImpl执行..."); haohanDao.add(); } }
4.在spring.xml中进行配置
上面我们创建了Dao层和Service层的接口类及其实现类,其中Service层的操作依赖于Dao层,下面通过Spring的IOC容器帮助我们创建并注入这些类。IOC使用的是XML配置文件
从xml文件中,我们需要声明一个beans的顶级标签,同时需要引入核心命名空间,Spring的功能在使用时都需要声明相对应的命名空间,上述的命名空间是最基本的。然后通过bean子标签声明那些需要IOC容器帮助我们创建的类,其中name是指明IOC创建后该对象的名称(当然也可以使用id替换name,这个后面会讲到),class则是告诉IOC这个类的完全限定名称,IOC就会通过这组信息利用反射技术帮助我们创建对应的类对象
通过property将HaohanDao注入给HaohanServiceImpl,这就要求HaohanServiceImpl中一定要有set方法,这个过程就是依赖注入
[code]<!--声明HaohanDao对象,交给spring创建--> <bean id="HaohanDao" class="org.westos.dao.HaohanImpl"></bean> <!--声明HaohanService对象,交给spring创建--> <bean id="HaohanService" class="org.westos.service.HaohanServiceImpl"> <!--注入HaohanDao对象,在HaohanServiceImpl中一定要有set方法--> <property name="HaohanDao" ref="HaohanDao"></property> </bean>
5.测试
[code] public static void main(String[]args){ ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); HaohanService haohanService = (HaohanService) applicationContext.getBean("HaohanService"); haohanService.doSomething(); }
三.spring依赖注入
依赖注入本质就是,当一个bean实例引用到另一个bean实例时,spring帮我们创建依赖bean实例,并注入(传递)到另一个bean实例中,
上例,HaohanService依赖于HaohanDao,spring 会创建HaohanService的实现类,和HaohanDao的实现类,并将HaohanDao的实现类注入到HaohanService的实现类中
注入方式有四种 构造方法的属性注入,setter注入,P名称空间的属性注入,SpringEl注入,常用的是setter注入
1.setter注入
Setter注入顾名思义,被注入的属性需要有set方法, Setter注入支持简单类型和引用类型,Setter注入时在bean实例创建完成后执行的。直接观察前面的案例,对象注入使用<property>的ref属性。
[code]<!--声明HaohanDao对象,交给spring创建--> <bean id="HaohanDao" class="org.westos.dao.HaohanImpl"></bean> <!--声明HaohanService对象,交给spring创建--> <bean id="HaohanService" class="org.westos.service.HaohanServiceImpl"> <!--注入HaohanDao对象,在HaohanServiceImpl中一定要有set方法--> <property name="HaohanDao" ref="HaohanDao"></property> </bean>
除了上述的对象注入同时也可以注入简单值和map、set、list、数组,简单值注入使用<property>的value属性:
Student类
[code]public class Student { private String name; private int age; private List<String> hobby; private Map<String,Integer> score; public List<String> getHobby() { return hobby; } public void setHobby(List<String> hobby) { this.hobby = hobby; } public Map<String, Integer> getScore() { return score; } public void setScore(Map<String, Integer> score) { this.score = score; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void hello(){ System.out.println("say hello"); } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
属性注入
[code] <bean id="Stu" class="org.westos.bean.Student"> <property name="name" value="张三"></property> <property name="age" value="19"></property> <property name="hobby"> <set> <value>唱歌</value> <value>看电影</value> </set> </property> <property name="score"> <map> <entry key="语文" value="99"></entry> <entry key="数学" value="145"></entry> </map> </property> </bean>
2.构造函数注入
构造注入也就是通过构造方法注入依赖,构造函数的参数一般情况下就是依赖项,spring容器会根据bean中指定的构造函数参数来决定调用那个构造函数
[code] <!--构造方法注入--> <bean id="car" class="org.westos.bean.Car"> <constructor-arg name="name" value="1"></constructor-arg> <constructor-arg name="price" value="13144"></constructor-arg> </bean>
四.IOC容器管理Bean
1.bean 标签的id和name
id :使用了约束中的唯一约束。里面不能出现特殊字符的。
name :没有使用约束中的唯一约束(理论上可以出现重复的,但是实际开发不能出现的)。里面可以出现特殊字符。
2.bean 的生命周期的配置(了解)
- init-method :Bean被初始化的时候执行的方法
- destroy-method :Bean被销毁的时候执行的方法(Bean是单例创建,工厂关闭)
customDao
[code]public interface CustomDao { void save(); }
customDaoImpl
[code]public class CustomDaoImpl implements CustomDao { public void setup(){ System.out.println("customDaoImpl初始化了"); } @Override public void save() { System.out.println("customDaoImplsave方法执行"); } private void destroy(){ System.out.println("customDaoImpl销毁了"); } }
spring.xml中生命周期的配置
[code]<!--生命周期的配置--> <!--将实现类交给spring管理--> <bean name="CustomDao" class="org.westos.dao.CustomDaoImpl" scope="singleton" init-method="setup" destroy-method="destroy"></bean>
测试
[code]public class Demo03 { public static void main(String[]args){ //生命周期 ClassPathXmlApplicationContext applicationContext= new ClassPathXmlApplicationContext("spring.xml"); CustomDao customDao = (CustomDao) applicationContext.getBean("CustomDao"); customDao.save(); // applicationContext.close(); //单例多例 CustomDao customDao2 = (CustomDao) applicationContext.getBean("CustomDao"); System.out.println(customDao==customDao2); } }
3.Bean的作用范围的配置(重点)
scope :Bean的作用范围
singleton :默认的,Spring会采用单例模式创建这个对象。
prototype :多例模式。(Struts2和Spring整合一定会用到)
request :应用在web项目中,Spring创建这个类以后,将这个类存入到request范围中。
session :应用在web项目中,Spring创建这个类以后,将这个类存入到session范围中。
globalsession :应用在web项目中,必须在porlet环境下使用。但是如果没有这种环境,相对于session。
4.spring的bean的实例化方式
bean已经交给了spring管理,spring在创建实例时,有几种方式
(1)无参构造方式
编写类
[code]public class Bean1 { //无参构造方法实现 public Bean1(){ System.out.println("Bean1的无参构造执行了"); } }
编写配置
[code]<!--无参构造方法--> <bean id="Bean1" class="org.westos.factroy.Bean1"></bean>
(2)静态工厂实例化的方式
[code]public class Bean2 { }
[code]public class Bean2Factroy { public static Bean2 creatBean2(){ System.out.println("BeanFactory执行了"); return new Bean2(); }
编写配置
[code] <!--静态工厂方法--> <bean id="Bean2" class="org.westos.factroy.Bean2Factroy" factory-method="creatBean2"></bean>
(3)实例工厂实例化的方式
[code]public class Bean3 { //实例工厂实例化方式 }
[code]public class Bean3Factroy { public Bean3 createBean3(){ System.out.println("Bean3Factory实例工厂"); return new Bean3(); } }
编写配置
[code] <!--实例工厂实例化--> <bean id="Bean3Factroy" class="org.westos.factroy.Bean3Factroy"></bean> <bean id="Bean3" factory-bean="Bean3Factroy" factory-method="createBean3"></bean>
五.IOC 与依赖注入的区别
IOC:控制反转:将对象的创建权,由Spring管理。
DI(依赖注入):在Spring创建对象的过程中,把对象依赖的属性注入到类中。
阅读更多- Spring的控制反转(IOC)和依赖注入(DI)详解
- 【JavaWeb-23】spring、IoC控制反转和DI依赖注入入门、基于XML的Bean装配、基于注解的Bean装配
- Spring学习(1):控制反转(IoC)和依赖注入(DI)的详解以及注解(annotation)开发入门案例
- IoC控制反转与DI依赖注入详解
- spring、IoC控制反转和DI依赖注入入门、基于XML的Bean装配、基于注解的Bean装配
- Spring学习(1):控制反转(IoC)和依赖注入(DI)的详解以及注解(annotation)开发入门案例
- Laravel 服务容器实例教程 —— 深入理解控制反转(IoC)和依赖注入(DI)
- Spring环境搭建之:控制反转(IoC Inversion of Control)与依赖注入(DI Depenency Injection)
- Spring的控制反转(IOC)和依赖注入(DI)具体解释
- IoC(Inversion of Control)控制反转和 DI(Dependency Injection)依赖注入
- Struts2_11_struts访问web元素2利用struts提供的三个接口_IoC控制反转设计思想/DI依赖注入
- 代码的演化-DI(理解依赖注入di,控制反转ioc)
- 如何理解Spring的控制反转IOC和依赖注入DI思想
- spring IOC(控制反转)和DI(依赖注入)以及三种依赖注入方式的比较
- 第二章 深入探讨控制反转(Ioc)和依赖注入(DI)
- 聊一聊PHP的依赖注入(DI) 和 控制反转(IoC)
- 解构控制反转(IoC)和依赖注入(DI)
- Ioc控制反转与DI依赖注入
- 利用反射简单模拟Spring的控制反转(Ioc)和依赖注入(DI)
- 有史以来最容易理解的控制反转(IoC)与注入依赖(DI)