Spring 学习笔记 (I) 之 Spring IoC

来源:互联网 发布:淘宝怎么换购产品 编辑:程序博客网 时间:2024/06/06 12:51
======================================
Spring 的基本用法

======================================

1、对于 Spring 开发者来说,Spring 配置文件就是核心。

2、Spring 提取了大量实际开发中需要重复解决的步骤,将这些步骤抽象成一个框架。

3、Spring 提供了一种 Template 的设计哲学,这些 Template 完成了大量的通用步骤,如果开发者使用这些 Template,则无须自己实现哪些通用步骤。

4、Spring 为企业应用的开发提供一个轻量级的解决方案。该解决方案包括:基于依赖注入的核心机制,基于 AOP 的声明式事务管理,与多种持久层技术的整合,以及优秀的 Web MVC 框架等。

5、Spring 致力于 Java EE 应用各层的的解决方案,而不是仅仅专注于某一层的方案。可以说:Spring 是企业应用开发的“一站式”选择,Spring 贯穿表现层、业务层、持久层。然而,Spring 并不想取代那些已有的框架,而是以高度的开放性与它们无缝整合。

6、不管是依赖注入,还是控制反转,其含义完全相同:当某个 Java 实例(调用者)需要另一个 Java 实例(被调用者)时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。

7、在依赖注入的模式下,创建被调用者的工作不再由调用者来完成因此称为控制反转创建被调用者实例的工作通常由 Spring 容器来完成,然后注入调用者因此也称为依赖注入

8、所谓依赖注入,是指程序运行过程中,如果需要另一个对象协作(调用它的方法、访问它的属性)时,无须在代码中创建被调用者,而是依赖于外部容器的注入。Spring 的依赖注入对调用者和被调用者几乎没有任何要求,完全支持对 POJO 之间依赖关系的管理。

9、依赖注入通常有如下两种
  • 设值注入:IoC 容器使用属性的 setter 方法来注入被依赖的实例。
  • 构造注入:Ioc 容器使用构造器来注入被依赖的实例。
10、建议采用以设值注入为主,构造注入为辅的注入策略。对于依赖关系无须变化的注入,尽量采用构造注入;而其他的依赖关系的注入,则考虑采用设值注入。

11、Spring 有两个核心接口:BeanFactory ApplicationContext,其中 ApplicationContext 是 BeanFactory 的子接口。它们都可代表 Spring 容器,Spring 容器是生成 Bean 实例的工厂,并管理容器中的 Bean。Bean 是 Spring 管理的基本单位,在基于 Spring 的 Java EE 应用中,所有的组件都被当成 Bean 处理,包括数据源、 Hibernate 的 SessionFactory、事务管理器等。

12、Spring 里的 Bean 是非常广义的概念,任何的 Java 对象、Java 组件都被当成 Bean 处理,甚至这些组件并不是标准的 JavaBean。

13、Spring 容器最基本的接口就是 BeanFactory,BeanFactory 负责配置、创建、管理 Bean,它有一个子接口:ApplicationContext,因此也被称为 Spring 上下文。Spring 容器还负责管理 Bean 与 Bean 之间的依赖关系

14、BeanFactory 有一个常用的实现类:org.springframework.beans.factory.xml.XmlBeanFactory 类。

15、ApplicationContext 是 BeanFactory 的子接口,对于大部分 Java EE 应用而言,使用它作为 Spring 容器更方便。其常用实现类是 FileSystemXMLApplicationContext、ClassPathXmlApplicationContext 和 AnnotationConfigApplicationContext。如果在 Web 应用中使用 Spring 容器,通常有 XmlWebApplicationContext、AnnotationConfigWebApplicationContext 两个实现类。

16、Resource 接口是 Spring 提供的资源访问接口,通过使用该接口,Spring 能以简单、透明的方式访问磁盘、类路径以及网络上的资源。

17、ApplicationContext 包括 BeanFactory 的全部功能,因此建议优先使用 ApplicationContext。除非对于某些内存非常关键的应用,才考虑使用 BeanFactory

18、当系统创建 ApplicationContext 容器时,默认会预初始化所有的 singleton Bean。也就是说,当 ApplicationContext 容器初始化完成后,容器中所有 singleton Bean 也实例化完成。这意味着:系统前期创建 ApplicationContext 时将有较大的系统开销,但一旦 ApplicationContext 初始化完成,程序后面获取 singleton Bean 实例时将拥有较好的性能。

19、ApplicationContext 的事件机制是观察者设计模式的实现,通过 ApplicationEvent 类和 ApplicationListener 接口,可以实现 ApplicationContext 的事件处理。如果容器中有一个 ApplicationListener Bean,每当 ApplicationContext 发布 ApplicationEvent 时,ApplicationListener Bean 将自动被触发。

20、Java 应用中各组件的相互调用的实质可以归纳为依赖关系,根据注入方式的不同,Bean 的依赖注入通常表现为如下两种形式。

  • 属性:通过<property.../>元素配置,对应设值注入。
  • 构造器参数:通过<constructor-arg.../>元素指定,对应构造注入。
21、不管是属性,还是构造器参数,都视为 Bean 的依赖,接受 Spring 容器管理,依赖关系的值要么是一个确定的值,要么是 Spring 容器中其他 Bean 的引用。

22、通常情况下,我们不应该使用配置文件管理普通的属性值;通常只使用配置文件管理容器中的 Bean 实例的依赖关系。

23、对于 singleton 作用域的 Bean,如果没有强行取消其预初始化行为,系统会再创建 Spring 容器时预初始化所有 singleton Bean,与此同时,该 Bean 所依赖的 Bean 也被一起实例化。

24、创建 BeanFactory 时不会立即创建 Bean 实例,所以有可能程序可以正确地创建 BeanFactory 实例,但当请求 Bean 实例时依然抛出一个异常:创建 Bean 实例或注入它的依赖关系时出现错误。

25、配置错误的延迟出现,也会给系统引入不安全因素,而 ApplicationContext 则默认预实例化所有 singleton 作用域 Bean,所以 ApplicationContext 实例化过程比 BeanFactory 实例化过程的时间和内存开销大,但可以在容器初始化阶段就检验出配置错误。

26、Spring 容器对 Bean 没有特殊要求,甚至不要求该 Bean 像标准的 JavaBean——必须为每个属性提供对应的 getter 和 setter 方法。Spring 中的 Bean 是 Java 实例、Java 组件;而传统的 Java 应用中的 JavaBean 通常作为DTO(数据传输对象),用来封装值对象,在各层之间传递数据

27、Spring 框架绝大部分工作都几种在对容器中 Bean 的管理上,包括管理容器中 Bean 的生命周期,使用 Bean 继承等特殊功能。通过这些深入的管理,应用程序可以更好地使用这些 Java 组件(容器中的 Bean 对应用而言,往往是一个组件)。

28、所有的抽象 Bean,就是指定 abstract 属性为 true 的 Bean,抽象 Bean 不能被实例化,Spring 容器不会创建抽象 Bean 的实例。抽象 Bean 的价值在于被继承,抽象 Bean 通常作为父 Bean 被继承。

29、子 Bean 无法从父 Bean 继承如下属性:depends-on、autowire、singleton、scope、lazy-init,这些属性将总是从子 Bean 定义中获得,或采用默认值。

30、Spring 中的 Bean 继承与 Java 中的继承截然不同。前者是实例与实例之间的参数的延续,后者则是一般到特殊的细化;前者是对象与对象之间的关系,后者是类与类之间的关系。Spring 中 Bean 的继承和 Java 中 Bean 的继承有如下区别

  • Spring 中的子 Bean 和父 Bean 可以是不同类型,但 Java 中的继承则可保证子类是一种特殊的父类。
  • Spring 中 Bean 的继承是实例之间的关系,因此主要表现为参数值的延续;而 Java 中的继承是类之间的关系,主要表现为方法属性的延续。
  • Spring 中 Bean 不可作为父 Bean 使用,不具备多态性;Java 中的子类实例完全可当成父类实例使用。
31、FactoryBean 接口是工厂 Bean 的标准接口,实现该接口的 Bean 通常只能作为工厂 Bean 使用,当我们将工厂 Bean 部署在容器中,并通过 getBean() 方法来获取工厂 Bean 时,容器不会返回 FactoryBean 实例,而是返回 FactoryBean 的产品。也就是说,当我们在 Spring 容器中部署一个工厂 Bean 之后,接下来程序尝试获取该工厂 Bean 时,程序所返回的并不是工厂 Bean 本身,而是该工厂 Bean 的 getObject() 方法的返回值。

32、当程序需要获得FactoryBean 本身时,并不直接请求 Bean id,而是在 Bean id 前增加 & 符号,容器则返回 FactoryBean 本身,而不是其产品 Bean。

33、对于 singleton 作用域的 Bean,Spring 容器知道 Bean 何时实例化结束、何时销毁,Spring 可以管理实例化结束之后和销毁之前的行为。管理 Bean 的生命周期行为主要有如下两个时机

  • 注入依赖关系之后。
  • 即将销毁 Bean 之前。

34、Spring 提供两种方式在 Bean 全部属性设置成功后执行特定行为:

  • 使用 init-method 属性。
  • 实现 initializingBean 接口。
35、如果某个 Bean 类实现了 InitializingBean 接口,当该 Bean 的所有依赖关系被设置完成后,Spring 容器会自动调用该 Bean 实例的 afterPropertiesSet() 方法。其执行结果与采用 init-method 属性指定生命周期方法几乎一样。但实现 InitializingBean 接口污染了代码,是侵入式设计,因此不推荐采用。

36、如果既采用 init-method 属性指定初始化方法,又实现 InitializingBean 接口来指定初始化方法,容器会执行两个初始化方法:先执行 InitializingBean 接口中定义的方法,然后执行 init-method 属性指定的方法

37、与定制初始化行为相似,Spring 也提供两种方法定制 Bean 实例销毁之前的特定行为,这两种方式如下:

  • 使用 destroy-method 属性。
  • 实现 DisposableBean 接口。
38、singleton 作用域的 Bean 通常会随容器的关闭而销毁,但问题是:ApplicationContext 容器在什么时候关闭呢?在基于 Web 的 ApplicationContext 实现中,系统已经提供了相应的代码保证关闭 Web 应用时恰当地关闭 Spring 容器。如果处于一个非 Web 应用的环境下,为了让 Spring 容器优雅地关闭,并调用 singleton Bean 上的相应析构回调方法,则需要在 JVM 里注册一个关闭钩子(shutdown hook),程序将会再退出 JVM 之前优雅地关闭 Spring 容器,并且保证关闭Spring 容器之前调用 singleton Bean 实例的析构回调方法。

39、协调作用域不同步的 Bean:使用<lookup-mehtod.../>元素需要指定如下两个属性。

  • name:指定需要让 Spring 实现的方法。
  • bean:指定 Spring 实现该方法后的返回值。
40、要保证 lookup 方法注入每次产生新的 Bean 实例,必须将目标 Bean(上例就是 steelAxe)部署成 prototype 作用域。否则,如果容器中只有一个目标 Bean 实例,即使采用 lookup 方法注入,每次依然返回同一个 Bean 实例。lookup 方法注入不仅能用于设值注入,也可用于构造注入。

41、滥用依赖注入也会引起一些问题。通常的建议是,组件与组件之间的耦合,采用依赖注入管理;但普通的 JavaBean 属性值,应直接在代码中设置。对于组件之间的耦合关系,通过使用控制反转,代码变得非常清晰。因此,Bean 无须管理依赖关系,而是由容器提供注入,Bean 无须知道这些实例在哪里,以及它们具体的实现。

42、在实际的应用中,某个实例的属性可能是某个方法的返回值,或者类的 Field 值,或者属性值,Spring 同样可以支持这种非常规的注入方式。Spring 甚至支持将 Bean 实例的属性值、方法返回值、Field 值,直接定义成容器中的一个变量。

43、在 Spring 配置文件中使用 XML 元素进行配置,实际上是让 Spring 执行相应的代码。例如:

  • 使用<bean.../>元素,实际上是让 Spring 执行无参数构造器、或有参数的构造器。
  • 使用<property.../>元素,实际上是让 Spring 执行一次 setter 方法。

但 Java 程序还可能有其他类型的语句:调用 getter 方法、调用普通方法、访问类或对象的 Field,而 Spring 也为这种语句提供了对应的配置语法。

  • 调用 getter 方法:使用 PropertyPathFactoryBean。
  • 访问类或对象的 Field 值:使用 FieldRetrievingFactoryBean。
  • 调用普通方法:使用 MethodInvokingFactoryBean。
让开发者无需书写 Java 代码就可进行 Java 编程,当开发者 XML 采用合适语法进行配置之后,Spring 就可通过反射在底层执行任意的 Java 代码

44、对于早期的基于 DTD 的 Spring 配置文件而言,Spring 用一种<bean.../>元素即可配置所有的 Bean 实例,而每个属性再用一个<property.../>元素即可,这种配置方式简单、直观,而且能以相同的风格处理所有 Bean 配置——唯一的缺点是配置复杂,但 Bean 实例的属性足够多且属性类型复杂(大多是集合属性时),基于 DTD 的配置文件将变得更加复杂。在这种情况下,Spring 提出了使用基于 XML Schema 的配置方式,这种配置方式更加简洁,可以对 Spring 配置文件进行“减肥”,但需要花一些时间来了解这种配置方式。

45、p 名称空间甚至不需要特定的 Schema 定义,它直接存在于 Spring 内核中。与前面采用<property.../>元素定义 Bean 的属性不同的是,当我们采用了 p 名称空间之后,就可直接再<bean.../>元素中使用属性来定义 Bean 实例的属性值了。

46、在 Spring 框架解压缩包的 projects\org.springframework.beans\src\main\resources\路径下包含大量 Schema 文件,例如 spring-beans-3.0.xsd、spring-tool-3.0.xsd 和 spring-util-3.0.xsd 等,在这些 Schema 中,只有 spring-beans-3.0.xsd 是 Spring 的内核,其他 Schema 大都用于简化某些方面的配置

47、Spring 表达式语言(简称 SpEL)是一种与 JSP 2 EL 功能类似的表达式语言,它可以在运行时查询和操作对象图。与 JSP 2 的 EL 相比,SpEL 功能更加强大,它甚至支持方法调用和基本字符串模板函数。

48、SpEL 可以独立于 Spring 容器使用——只是当成简单的表达式语言来使用;也可以在 Annotation 或 XML 配置中使用 SpEL,这样可以充分利用 SpEL 简化 Spring 的 Bean 配置。

49、Spring 的 SpEL 可以单独使用,可以使用 SpEL 对表达式计算、求值。SpEL 主要提供了如下两个接口。

  • ExpressionParser:该接口的实例负责解析一个 SpEL 表达式,返回一个 Expression 对象。
  • Expression:该接口的实例代表一个表达式。
50、T() 运算符使用 java.lang 包下的类时可以省略包名,但使用其他包下的所有类时应使用全限定类名。

51、SpEL 允许通过 EvaluationContext 来使用变量,该对象包含了一个 setVariable(String name, Object value)方法,该方法用于设置一个变量。一旦在 EvaluationContext 中设置了变量,就可以在 SpEL 中通过 #name 来访问该变量。

52、值得指出的是 SpEL 中有如下两个特殊的变量。

  • #this:引用 SpEL 当前正在计算的对象。
  • #root:引用 SpEL 的 EvaluationContext 的 root 对象。
0 0
原创粉丝点击