(二)Spring与IOC

笑着哭i 提交于 2019-12-17 01:25:28

  控制反转(IOC)是一个概念、是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本省反转到了外部容器。但是,需要注意,Ioc也是有局限性的,其不能使用在分布式系统中。即其所依赖的反转到的外部容器,必须要与控制权出让方同处于一个JVM中。

  IOC是一个概念,是一种思想,其实现方式多种多样。当前比较流行的实现方式有两种:依赖注入和依赖查找。依赖注入方式应用更为广泛。

  • 依赖查找:容器提供调接口上下文环境给组件,程序代码则需要提供具体的查找方式。比较典型的是依赖于JNDI系统的查找。
  • 依赖注入:程序代码不做定位查询,这些工作由容器自行完成。

  依赖注入只指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。

  Spring的依赖注入对调用者与被调用者几乎没有任何要求,完全支持POJO之间依赖关系的管理。

  依赖注入是目前最优秀的解耦方式。依赖注入让Spring的Bean之间以配置文件的方式组织在一起,而不是以硬编码的方式耦合在一起。

一、SpringDI的引入

  1、SpringDI的底层实现原理

  Spring DI = 工厂 +  反射 + 配置文件

  传统开发中的面向抽象编程,调用者与接口实现类是紧密耦合在一起的。若实现类发生改变,则必须修改原有代码:在调用者类中将实现类进行更换。但这不符合软件开发的OCP原则。

  OCP原则,即开闭原则,指软件实体(类、模块、功能等)应该可以被扩展,但不可被修改。即对功能扩展是开放的,对已有代码修改是关闭的。也就是说,应该在不修改现有代码的基础上,扩展新功能。

  要使调用者类与接口实现类之间实现解耦,可使用工厂模式。但,这又使调用者与工厂耦合,工厂与接口实现类耦合。实现类的更换,不需要修改调用者类,但需要修改工厂,即代码仍需重新编译。

  此时,可以通过在工厂类中使用“反射+配置文件”的方式来加载接口实现类。而这就是Sprin的IOC的工作原理

  2、普通三层架构编程

  举例:common

  Step1:创建一个Java Project

  Step2:创建Service层接口StudentService

  Step3:创建接口实现类StudentServiceImpl

  Step4:  创建Dao层接口StudentDao

  Step5:创建接口实现类StudentDaoImpl

  Step6:编写测试类MyTest

  3、工厂模式编程

  举例:factory

  Step1:复制common项目,更名为factory

  Step2:创建工厂类ServiceFactory与DaoFactory

  Step3: 修改MyTest中Service的获取方式

  Step4:修改StudentServiceImpl中Dao的获取方式

  4、工厂模式+反射编程

  举例:reflect

  Step1:复制factory项目,更名为reflect

  Step2:修改Service工厂类:使用反射创建Service类对象

  

  Step3:修改Dao工厂类:使用反射创建Dao实现类对象

 二、SPring程序开发

  在普通三层架构的基础上,将程序修改为SPring框架程序例子,举例:springDemo

  1、导入Jar包

    首先,导入Spring程序开发的四个基本Jar包。

  

  其次,导入日志相关的Jar包。由以下依赖库中:

  

  2、创建Spring配置文件

  Spring配置文件的文件名可以随意,但Spring建议的名称可以随意,但Spring建议的名称为applicationContext.xml。

  注意,Spring配置文件中使用的约束文件为xsd文件。这里需要的是spring-beans.xsd约束文件,故需要在beans子目录中查找相应版本的约束文件。

  这里需要的是spring-beans.xsd约束文件,故需要在beans子目录版本中查找相应的约束文件。

  

  

  <bean />用于定义一个实例对象。一个实例对应一个bean元素。

  id:该属性是Bean实例的唯一标识,程序通过id属性访问Bean,Bean与Bean间的依赖关系也是通过id属性关联的。

  class:指定该Bean所属的类,注意这里只能是类,不能是接口。

  3、定义测试类

  

  (1)ApplicationContext接口容器

    ApplicationContext用于加载Spring的配置文件,在程序中充当"容器"的角色。其实现类有两个。通过Ctrl+T查看。

    

  A、配置文件在类路径下

    若Spring配置文件存放在项目的类路径下,则使用ClassPathXmlApplicationContext实现类进行加载。

      

  B、配置文件在本地目录中

    若Spring配置文件存放在本地磁盘目录中,则使用FileSystemXmlApplicationContext实现类进行加载。

  

  (2)BeanFactory接口容器

     BeanFactory接口对象也可作为Spring容器出现。BeanFactory接口是ApplicationContext接口的父类。

    

      若要创建BeanFactory容器,需要使用实现类XmlBeanFactory(Ctrl+T查看继承关系)。该类可以加载Spring配置文件

    

    而Spring配置文件以资源Resource的形式出现在XmlBeanFactory类的构造器参数中。Resource是一个接口,其具有两个实现类:

  •  ClassPathResource:指定类路径下的资源文件
  •    FileSystemResource:指定项目跟路径或本地磁盘路径下的资源文件

  在创建了BeanFactory容器后,便可以使用其重载的getBean()方法,从容器中获取指定Bean对象。

  

  

  (3)两个接口容器的区别

    虽然这两个接口容器所要加载的Spring配置文件是同一文件,但在代码中的这两个容器对象却不是同一对象,即不是同一个容器:它们对于容器内对象的装配(创建)时机是不同的。

    装配时机测试时需要注意,首先要在容器中对象StudentServiceImpl类的无参构造器中添加一个输出语句,以显示其是否执行。

    

  A.ApplicationContext容器中对象装配时机

  ApplicationContext容器,会对容器对象初始化时,将其中所有对象一次性全部装好。以后代码中若要使用到这些对象,只需从内存中直接获取即可。执行效率高。但占用内存。

  

  B、BeanFactory容器中对象的装配时机

  BeanFactory容器,对容器中对象的装配与加载采用延迟加载策略,即在第一次调用getBean()时,才真正装配该对象。

  

 三、Bean的装配

  举例:beanAssemble项目

  Bean的装配,即Bean对象的创建。容器根据代码要求创建Bean对象后再传递给代码的过程,称为Bean的装配。

  1、默认装配方式

  代码通过getBean()方式从容器获取指定的Bean实例,容器首先会调用Bean类的无参构造器,创建空值的实例对象。

  举例:ba01包

  

  

  

  

 2、动态工厂Bean

  有些时候,项目中需要通过工厂类来创建Bean实例,而不能像前面例子中似的,直接由Spring容器来装配Bean实例。使用工厂模式来创建bean实例,就会使工厂类与要创建的Bean类耦合到一起。

 (1)将动态工厂Bean作为普通Bean使用

  将动态工厂Bean作为普通Bean来使用是指,在配置文件中注册过动态工厂Bean后,测试类直接通过getBean()获取到工厂对象,再由工厂对象调用其相应方法创建相应的目标对象。配置文件中无需注册目标对象的Bean。因为目标对象的创建不由Spring容器来管理。

  举例ba02包

  

  

  

  (2)使用Spring的动态工厂Bean

  Spring对于使用动态工厂来创建Bean,有专门的属性定义。factory-bean指定相应的工厂Bean,由factory-method指定创建所用的方法。此时配置文件中至少有两个Bean的定义:工厂类的Bean,与工厂类所要创建目标类Bean。而测试类中不再获取工厂Bean对象了,可以直接获取目标Bean对象。实现测试类与工厂类间的解耦。

3、静态工厂Bean

  使用工厂模式中静态工厂来创建实例Bean。

  此时需要注意,静态工厂无需工厂实例,所以不再需要定义静态工程<bean/>。

  面对于工厂所要创建的Bean,其不是由自己的类创建的,五哦一无需指定自己类。但是是由工厂类创建的,所以需要指定所用工厂类。故class属性指定的是工厂类而非自己的类。当然,还需要通过favtory-method属性指定工厂方法。

  举例:ba03包

  

  

4、容器Bean的作用域

  当通过Spring容器创建哟i个Bean实例时,不仅可以完成Bean的实例化,还可以通过scope属性,为Bean指定特定的作用域。Spring支持种作用域。

(1)singleton:单态模式。即在整个Spring容器中,使用singleton定义的Bean将是单例的,只有一个实例。默认为单态的。

(2)prototype:原型模式。即每次使用getBean方法获取的同一个<bean/>的实例都是一个新的实例。

(3)request:对于每次HTTP请求,都将会产生一个不同的Bean实例。

(4)session:

(5)globa session:

 注意:

 (1)对于scope的值request、session与global session,只有在Web应用中使用Spring时,该作用域才有效。

 (2)对于scope为singleton的单例模式,该Bean是在容器被创建时即被装配好了。

 (3)对于scope为prototype的原型模式,Bean实例是在代码中使用该Bean实例才进行装配的。

 举例:ba04包

  

  

5、Bean后处理器

  Bean后处理器是一种特殊的Bean,容器中所有的Bean在初始化时,均会自动执行该类的两个方法。由于该Bean是由其它Bean自动调用执行,不是程序员手工调用,故此Bean无须id属性。

  需要做的是,在Bean后处理器类方法中,只要对Bean类与Bean类中的方法进行判断,就可实现对指定的Bean的指定方法进行功能扩展与增强。方法返回的Bean对象,即是增过的对象。

  代码中需要

6、定制Bean的生命始末

 可以为Bean定制初始化后的生命行为,也可以为Bean定制销毁前的生命行为。

 举例:ba06包。

 首先,这些方法需要在Bean类中事先定义好:是方法名随意的public void方法。

 

  其次,在配置文件的<bean/>标签中增加如下属性:

  init-method:指定初始化方法的方法名

  destory-method:指定销毁方法的方法名

  

  注意,若要看到Bean的destroy-method的执行结果,需要满足两个条件:

  (1)Bean为singleton,即单例

  (2)要确保容器关闭。接口ApplicationContext没有close()方法,但其实现类由。所以,可以将ApllicationContext强转为其实现类对象,或直接创建的就是实现类对象。

  

7、Bean的生命周期

  Bean实例从创建到最后销毁,需要经过很多过程,执行很多生命周期方法。

  Step1:调用无参构造器,创建实例对象。

  Step2:  调用参数的setter,为属性注入值。

  Step3:若Bean实现了BeanNameAware接口,则会执行接口方法setBeanName(String beanId),使Bean类可以获取在容器的id名称。

  Step4:若Bean实现了BeanFactoryAware接口,则执行接口方法setBeanFactory(BeanFactory factory),使Bean类可以获取BeanFactory对象。

  Step5:若定义并注册了Bean后处理器BeanPostProcessor,则执行接口方法postProcessBeforeInitialization()。

  Step6:若Bean实现了 InitializingBean接口,则执行接口方法afterPropertiesSet()。该方法在Bean的所有属性的set方法set方法执行完毕后执行,是Bean初始化结束的标志,即Bean实例化结束。

  Step7:若设置了init-method方法,则执行。

  Step8:若定义并注册了Bean后处理器BeanPostProcessor,则执行接口方法postProcessAfterInitialization()。

  Step9:  执行业务方法

  Step10:若Bean实现了DisposableBean接口,则执行接口方法destory()。

  Step11: 若设置了destroy-method方法,则执行。

  举例:ba07包

  

8、<bean/>标签的id属性与name属性

  一般情况下,命名<bean/>使用id属性,而不使用name属性。在没有id属性的情况下,name属性与id属性作用是相同的。但当<bean/>中含有一些特殊字符时,就需要使用name属性了。

  id的命名需要满足XML对ID属性命名规范:必须以字母开头,可以包含字母、数字、下划线等。且要求名称在容器中必须唯一。

  name则可以包含各种字符,且对名称没有唯一要求。若名称不唯一,则后面的会覆盖前面的。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!