Spring IOC 学习总结

1 什么是IOC、DI

  IoC—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建。

  IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

  其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。

  DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

  理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:

  ●谁依赖于谁:当然是应用程序依赖于IoC容器;

  ●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;

  ●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;

  ●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

  IoC和DI有什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。

  IoC技术是Spring的核心,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。

  所以控制反转IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系。

  注意,一般将BeanFactory称为IoC容器,而称ApplicationContext和WebApplicationContext为应用上下文或Web应用上下文。在本篇博客中,把WebApplicationContext和ApplicationContext统称为Spring容器。

2 IoC相关的Java基础知识

2.1 反射

  IOC中最基本的技术就是“反射(Reflection)”。反射机制: 反射是java语言的一个特性,它允程序在运行时(注意不是编译的时候)来进行自我检查并且对内部的成员进行操作。例如它允许一个java的类获取它所有的成员变量和方法并且显示出来。通过这种能力可以彻底的了解自身的情况为下一步的动作做准备。Java的反射机制的实现要借助于4个类:class,Constructor,Field,Method;其中class代表的时类对象,Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象。通过这四个对象我们可以粗略的看到一个类的各个组 成部分。

  Java反射的作用:在Java运行时环境中,对于任意一个类,可以知道这个类有哪些属性和方法。对于任意一个对象,可以调用它的任意一个方法。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。

  Java 反射机制主要提供了以下功能,在运行时判断任意一个对象所属的类。在运行时构造任意一个类的对象。在运行时判断任意一个类所具有的成员变量和方法。在运行时调用任意一个对象的方法。

  从Java内存模型的角度理解反射,首先上一张Java内存模型的神图:

  

  

  首先我们了解一下JVM,什么是JVM,Java的虚拟机,java之所以能跨平台就是因为这个东西,你可以理解成一个进程,程序,只不过他的作用是用来跑你的代码的。

  假如你写了一段代码:Object o=new Object(); 运行了起来!

  首先JVM会启动,你的代码会编译成一个.class文件,然后被类加载器加载进jvm的内存中,你的类Object加载到方法区中,创建了Object类的class对象到堆中,注意这个不是new出来的对象,而是类的类型对象,每个类只有一个class对象,作为方法区类的数据结构的接口。jvm创建对象前,会先检查类是否加载,寻找类对应的class对象,若加载好,则为你的对象分配内存,初始化也就是代码:new Object()。

  上面的流程就是你自己写好的代码扔给jvm去跑,跑完就over了,jvm关闭,你的程序也停止了。

  为什么要讲这个呢?因为要理解反射必须知道它在什么场景下使用。大家想想上面的程序对象是自己new的,程序相当于写死了给jvm去跑。假如一个服务器上突然遇到某个请求哦要用到某个类,哎呀但没加载进jvm,是不是要停下来自己写段代码,new一下,哦启动一下服务器,(脑残)!

  反射是什么呢?当我们的程序在运行时,需要动态的加载一些类这些类可能之前用不到所以不用加载到jvm,而是在运行时根据需要才加载,这样的好处对于服务器来说不言而喻,举个例子我们的项目底层有时是用mysql,有时用oracle,需要动态地根据实际情况加载驱动类,这个时候反射就有用了,假设 com.java.dbtest.myqlConnection,com.java.dbtest.oracleConnection这两个类我们要用,这时候我们的程序就写得比较动态化,通过Class tc = Class.forName("com.java.dbtest.TestConnection");通过类的全类名让jvm在服务器中找到并加载这个类,而如果是oracle则传入的参数就变成另一个了。这时候就可以看到反射的好处了,这个动态性就体现出java的特性了。

  反射可以实现动态创建对象和编译,体现出很大的灵活性(特别是在J2EE的开发中它的灵活性就表现的十分明显)。通过反射机制我们可以获得类的各种内容,进行了反编译。对于JAVA这种先编译再运行的语言来说,反射机制可以使代码更加灵活,更加容易实现面向对象。反射机制的缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它 满足我们的要求。这类操作总是慢于只直接执行相同的操作。

  Spring的IOC正是反射技术最好的舞台,IOC中大量利用反射技术,通过配置XML或者@Annotation的方式配置装配bean到Spring容器,程序运行起来后,根据需要,Spring容器在动态的注入bean到需要的对象中。

  那么,Spring是如何装载XML文件以及资源文件的呢?在2.2中介绍。

2.2 资源访问利器:Spring Resource接口

  JDK所提供的访问资源的类(如java.net.URL、File等)并不能很好地满足各种底层资源的访问需求,比如缺少从类路径或者Web容器的上下文中获取资源的操作类。鉴于此,Spring设计了一个Resource接口,它为应用提供了更强的底层资源访问能力。Spring的各种资源加载,都是通过这个接口来实现的

  Resource接口的主要方法有

    boolean exists():资源是否存在。

    boolean isOpen():资源是否打开。

    URL getURL():如果底层资源可以表示为URL,该方法返回对应的URL对象。

    File getFile():如果底层资源对应一个文件,该方法返回对应的File对象。

    InputStream getInputStream() throws IOException:返回资源对应的输入流。

  为了可以在不显示使用Resource实现类的情况下,仅通过资源地址的特殊标识就可以加载相应的资源,Spring提供了一个强大的加载资源的机制,可以通过"classpath:","file:"等资源地址前缀识别不同的资源类型,还支持ant风格的带通配符的资源地址。

  

3 BeanFactory和ApplicationContext

  什么是POJO,什么是Java Bean,什么是Bean?

  1)POJO = "Plain Old Java Object",是MartinFowler等发明的一个术语,用来表示普通的Java对象,个人理解就是含有一些私有属性,以及getter/setter/constructer方法的普通java类。

  2)Java Bean比POJO要复杂一些,需要满足一些规范,例如:需要实现 Serializable 接口用于实现 Bean 的持久性。

  3)Bean比Java Bean更宽泛一些,所有可以被Spring容器实例化并管理的Java类都可以称之为Bean。

  Spring通过一个配置文件描述Bean及Bean之间的依赖关系,利用Java语言的反射功能实例化Bean并建立Bean之间的依赖关系。Spring的IOC容器在完成这些底层工作的基础上,还提供了Bean实例缓存、生命周期管理、Bean实例代理、事件发布、资源装载等高级服务。

  BeanFactory是Spring框架最核心的接口,它提供了高级Ioc的配置机制。ApplicationContext建立在BeanFactory基础之上,提供了更多面向应用的功能,它提供了国际化支持和框架事件体系,更易于创建实际应用。两者都可称为Spring容器。

  二者的用途,一般可以进行简单的划分:BeanFactory是Spring框架的基础设施,面向Spring本身;ApplicationContext面向使用Spring框架的开发者,几乎所有的应用场合都可以直接使用ApplicationContext而非底层的BeanFactory。

3.1 WebApplicationContext

  WebApplicationContext专门为Web应用准备的,它允许从相对于Web根目录的路径中装在配置文件(web.xml)完成初始化工作。从WebApplicationContext中可以获得ServletContext的引用,整个Web应用上下文对象将作为属性放置到ServletContext中,以便Web应用环境可以访问Spring应用上下文。

  在非Web应用的环境下,Bean只有singleton和prototype两种作用域,在WebApplicationContext中又添加了三个新的作用域:request、session和global session。

  WebApplicationContext的初始化方式和BeanFactory、ApplicationContext有所区别,因为WebApplicationContext需要ServletContext实力,也就是说,它必须在拥有Web容器(Tomcat、Jetty等)的前提下才能完成启动工作。可以在web.xml中配置自启动的Servlet或定义Web容器监听器(ServletContextListner),借助两者中的任何一个,就可以完成启动Spring Web应用上下文的工作。

  自启动的Servlet启动WebApplicationContext:

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0"
         metadata-complete="true">
  <!--用maven创建的web-app需要修改servlet的版本为3.1-->
  <!--配置DispatcherServlet-->
  <servlet>
    <servlet-name>seckill-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--
        配置SpringMVC 需要配置的文件
        spring-dao.xml,spring-service.xml,spring-web.xml
        Mybites -> spring -> springMVC
    -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring/spring-*.xml</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>seckill-dispatcher</servlet-name>
    <!--默认匹配所有请求-->
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

  通过Web容器监听器启动WebApplicationContext:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:smart-context.xml</param-value>
    </context-param>
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

    <servlet>
        <servlet-name>smart</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <load-on-startup>3</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>smart</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
</web-app>

4 Bean作用域

  Scope,也称作用域,在 Spring IoC 容器是指其创建的 Bean 对象相对于其他 Bean 对象的请求可见范围。在 Spring IoC 容器中具有以下几种作用域:基本作用域(singleton、prototype),Web 作用域(reqeust、session、globalsession),自定义作用域。

    • singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例
    • prototype:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例
    • request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效
    • session:对于每次HTTP Session,使用session定义的Bean豆浆产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效
    • globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效

  其中比较常用的是singleton和prototype两种作用域。对于singleton作用域的Bean,每次请求该Bean都将获得相同的实例。容器负责跟踪Bean实例的状态,负责维护Bean实例的生命周期行为;如果一个Bean被设置成prototype作用域,程序每次请求该id的Bean,Spring都会新建一个Bean实例,然后返回给程序。在这种情况下,Spring容器仅仅使用new 关键字创建Bean实例,一旦创建成功,容器不在跟踪实例,也不会维护Bean实例的状态。

  如果不指定Bean的作用域,Spring默认使用singleton作用域。Java在创建Java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此,prototype作用域Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,可以重复使用。因此,除非必要,否则尽量避免将Bean被设置成prototype作用域。

5 IoC Bean的4种配置方式比较

类别 基于XML配置 基于注解配置 基于Java类配置 基于Groovy DSL配置
Bean定义 在XML文件中通过<bean>元素定义Bean,如:<bean class="com.xgj.UserDao"/> 在Bean实现类处通过标注@Component或衍型类@Repository、@Service及@Controller定义Bean 在标注了@Configuration的Java类中,通过在类方法上标注@Bean定义一个Bean。方法必须提供Bean的实例化逻辑 在Groovy 文件中通过DSL定义Bean的名称 ,如 userDao(UserDao)
Bean名称 通过<bean>的id或name属性定义,如:<bean id="userDao" class="com.xgj.UserDao"/> 默认名称为:com.xgj.userDao#0 通过注解的value属性定义,如@Component(“userDao”)。默认名称为小写字母打头的类名(不带包名):userDao 通过@Bean的name属性定义,如@Bean(“userDao”),默认名称为方法名 通过GroovyDSL定义Bean的名称
Bean注入 通过<property>子元素或通过p命名空间的动态属性,如p:userDao-ref=”userDao”进行注入 通过在成员变量或方法入参处标注@Autowired,按类型匹配自动注入。还可以配合使用@Qualifier按名称匹配方式注入 比较灵活,可以通过在方法处通过@Autowired方法入参绑定Bean,然后在方法中通过代码进行注入,还可以通过调用配置类的@Bean方法进行注入 比较灵活,可以在方法出通过ref()方法进行注入,如ref(“logDao”)
Bean生命过程方法 通过<bean>的init-method和destory-method属性指定Bean实现类的方法名。最多只能指定一个初始化方法和一个销毁方法。 通过在目标方法上标注@PostConstruct和@PreDestroy注解指定初始化或销毁方法,可以定义任意多个方法 通过@Bean的initMethod或destoryMethod指定一个初始化或销毁方法.对于初始化方法来说,你可以直接在方法内部通过代码的方式灵活定义初始化逻辑 通过bean->bean,initMehtod或者bean.destoryMethod指定一个初始化或者销毁方法
Bean作用范围 通过<bean>的scope属性指定,如:<bean class="com.xgj.UserDao" scope="prototype"/> 通过在类定义处标注@Scope指定,如@Scope(“prototype”) 通过在Bean方法定义处标注@Scope指定 通过bean->bean,scope=”prototype”指定
Bean延迟初始化 通过<bean>的lazy-init属性指定,默认为default,继承于的default-lazy-init设置,该值默认为false 通过在类定义处标注@Lazy指定,如@Lazy(true) 通过在Bean方法定义处标注@Lazy指定 通过bean->bean.lazyInit-true指定

  我在项目中一般采用“XML+注解”的配置方式,DataSource、JdbcTemplate由于无法在类中标注注解,所以通过XML配置方式比较好,命名空间:如aop、context等,只能采用基于XML的配置。Bean的实现类是当前项目开发的,可以直接在Java类中使用基于注解的配置,例如说Service层实现类可以标注@Service,Controller实现类标注@Controller,配合@Autowired就可以很好地使用基于注解的配置进行Bean的定义和注入。

6 IoC 在Spring Web中的应用

  个人理解,一个Spring Web应用中,是从web.xml开始加载的,通过WebApplicationContext中加载web.xml可以获得ServletContext的引用,读取到web.xml中其他Spring配置文件的路径,再通过Spring Resource接口加载配置文件,根据注解扫描或者XML配置实例化、注入Bean到WebApplicationContext(Spring 容器),整个Spring 容器(WebApplicationContext对象)将作为属性放置到Web容器(ServletContext)中,以便Web容器可以访问Spring容器中的内容。

参考资料:

https://blog.csdn.net/qq_22654611/article/details/52606960/

https://www.zhihu.com/question/24304289/answer/76541818

https://www.zhihu.com/question/24304289/answer/147529485

https://blog.csdn.net/u011468990/article/details/49995865

《精通Spring 4.x 企业应用开发实战》 陈雄华 林开雄 文建国 编著

原文地址:https://www.cnblogs.com/kukri/p/9034912.html

时间: 2024-10-09 13:51:51

Spring IOC 学习总结的相关文章

Spring Ioc学习(一)

包含内容:什么是Ioc,作用,bean的创建,方式,配置,集合,作用域,不同作用与的bean依赖等 Ioc控制反转(Inversion of Control) 也被叫作DI依赖注入(Dependency Injection).在程序设计中经常使用到组合的形式,就是在一个类中使用到其他类的对象.Ioc就是用来当这个类实例化时,它里面所使用到的相关对象也会被实例化.实例化的方式有①构造器参数②方法参数③属性. Spring容器启动是通过BeanFactory或ApplicationContext读取

Spring Ioc学习(二)

wxWidgets和Codeblocks的编译安装,GUI程序开发平台的搭建具体步骤如下: (1)基本编译环境安装 安装编译工具(gcc之类)sudo apt-get install build-essential 安装X11sudo apt-get install libx11-dev 安装GTK需要的东西sudo apt-get install?gnome-core-devel (2)下载wxWidgets源码包并解压缩到 #{wxdir} (3)创建基于gtk和x11的编译目录${wx}

Spring框架学习之IOC(一)

Spring框架学习之IOC 先前粗浅地学过Spring框架,但当时忙于考试及后期实习未将其记录,于是趁着最近还有几天的空闲时间,将其稍微整理一下,以备后期查看. Spring相关知识 spring是J2EE应用程序框架,是轻量级的IoC和AOP的容器框架,主要是针对javaBean的生命周期进行管理的轻量级容器.可单独使用,通常也与其他框架整合使用,例如SSH.SSM. IOC:控制反转 控制权由对象本身转向容器:由容器根据配置文件去创建实例并创建各个实例之间的依赖关系.等同于DI依赖注入 A

菜鸟学习spring IOC有感

 一.spring IOC思想引入 事实上对于刚開始学习的人来说,在学习IOC的时候确实有点困难,主要是掌握其思想方面存在一丢丢的障碍,可是假设可以跨过这个障碍,则可以高速掌握当中的思想了.单从字面上来讲,事实上IOC(反向控制)指的就是控制方向发生了变化.我们常常会遇到这句话:"实现必须依赖抽象,而不是抽象依赖实现."尽管这句话表达了反向控制的概念,可是对于刚開始学习的人来讲,确实不是非常好理解.接下来我们就通过一些实例去理解这些内容的含义. 首先我们创建两个类,一个用于连接数据

Spring框架学习之IOC(二)

Spring框架学习之IOC(二) 接着昨天的内容,下面开始IOC基于注解装配相关的内容 在 classpath 中扫描组件 <context:component-scan> 特定组件包括: –@Component: 基本注解, 标识了一个受 Spring 管理的组件 –@Respository: 标识持久层组件 –@Service: 标识服务层(业务层)组件 –@Controller: 标识表现层组件 对于扫描到的组件, Spring 有默认的命名策略: 使用非限定类名, 第一个字母小写.

Spring?IOC设计原理解析:本文乃学习整理参考而来

Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. IoC容器的初始化 1. XmlBeanFactory(屌丝IOC)的整个流程 2. FileSystemXmlApplicationContext 的IOC容器流程 1.高富帅IOC解剖 2. 设置资源加载器和资源定位 3.AbstractApplicationContext的refresh函数载入

Spring IOC及AOP学习总结

一.Spring IOC体系学习总结: Spring中有两个容器体系,一类是BeanFactory.另一类是ApplicationContext.BeanFactory提供了基础的容器功能.ApplicationContext则是基于BeanFactory建立的一套更加丰富的容器体系,基于ApplicationContext构建了Spring AOP体系(基于AOP体系又构建了声明式事务模型),I18n的支持,基于观察者模式的事件模型,多渠道的Bean资源的载入(比如从文件系统,从interne

从零开始手写 spring ioc 框架,深入学习 spring 源码

IoC Ioc 是一款 spring ioc 核心功能简化实现版本,便于学习和理解原理. 创作目的 使用 spring 很长时间,对于 spring 使用非常频繁,实际上对于源码一直没有静下心来学习过. 但是 spring 源码存在一个问题,那就是过于抽象,导致学习起来成本上升. 所以本项目由渐入深,只实现 spring 的核心功能,便于自己和他人学习 spring 的核心原理. spring 的核心 Spring 的核心就是 spring-beans,后面的一切 spring-boot,spr

学习spring ioc的几点建议

写在前面的话Spring 是一个轻量级的企业级应用开发框架,经年累月的迭代spring已经包含了很多模块.其中包括springBoot,springframework,springdata,springcloud,博主想写一点关于spring全家桶的东西包括spring全家桶技术应用,如何看spring官网学习,如何看spring的源码.但是写博客是个技术活,而且东西比较多,希望我可以坚持下来每天写一点.本文是基于spring5的知识分析,比如后面的源码.比如官网.如果你看到的源码和官网和博文有