Spring(2)—IOC

时间:2023-03-08 21:17:27

一、Spring IOC是什么

1.简述

Spring是一个开源框架

Spring为简化企业级应用开发而生,使用Spring可以使简单的JavaBean实现以前只有EJB才能实现的功能

Spring是一个IOC(DI)和AOP容器框架

2.特性描述

轻量级:Spring是非侵入式的,基于Spring开发的应用中对象可以不依赖于Spring的API

依赖注入(DI—dependency injection、IOC)

面向切面编程(AOP—aspect oriented programming)

容器:Spring是一个容器,因为它包含并管理应用对象的生命周期

框架:Spring实现了使用简单的组件配置组合成一个复杂的应用,在Spring中可以使用XML和java注解组合这些对象

一站式:在IOC和AOP的基础上可以整合各种企业级应用的开源框架和优秀的第三方应用(实际上Spring自身也提供了展现

层的SpringMVC和持久层的Spring JDBC)

3.IOC和DI概述

IOC:其思想是反转资源获取的方向,传统的资源查找方式要求组件向容器发起请求查找资源,作为回应,容器适时的返回资源,而应用

了IOC之后,则是容器主动地将资源推送给它所管理的组件,组件所要做的就是选择一种合适的方式接受资源,这种行为被称为查找的被动形式。

DI:IOC的另一种表述形式:即组件以一些预先定义好的方式(例如:setter方法)接受来自容器的资源注入,相对于IOC而言,

这种表述更直接。

4.如何配置Bean

        ——IOC容器BeanFactory、ApplicationContext概述
——配置形式:基于xml文件的方式、基于注解的方式
——Bean的配置方式:通过全类名(反射)、通过工厂方法(静态工厂方法&实例工厂方法)、FactoryBean
——依赖注入的方式:属性注入、构造器注入
——属性注入细节
——自动装配
——Bean之间的关系:继承、依赖
——Bean的作用域:singleton、prototype、web环境作用域
——使用外部属性文件
——spEL
——IOC容器中Bean的生命周期
Spring4.x新特性:泛型依赖注入

5.Spring容器

①在Spring容器读取Bean配置创建Bean实例之前,必须对它进行实例化,只有在容器实例化之后,才可以从容器中获取Bean实例
并使用。
②Spring提供了两种类型的IOC容器实现:Beanfactory、ApplicationContext。
③BeanFactory是Spring框架的基础设施,面向Spring本身
④ApplicationContext面向使用Spring框架的开发者,几乎所有的应用场合都直接使用ApplicationContext而
非底层的Beanfactory,无论使用那种方式,配置文件都是相同的。
⑤ApplicationContext:有两个实现类
——ClassPathXmlApplicationContext:从类路径下加载配置文件
——FileSystemXmlApplicationContext:从文件系统中加载配置文件

6.依赖注入的方式

Spring支持3种依赖注入的方式

属性注入、构造器注入、工厂方法注入很少使用,不推荐)

①属性注入:即通过setter方法注入Bean的属性或依赖的对象,属性注入使用<property>
元素,使用name属性指定Bean的属性名称,value属性或<value>子节点指定属性值,属性注入是
最长用的注入方式。
<bean id="helloWorld class="com.test.spring.bean.HelloWorld">
<!-- name的值对应着,类HelloWorld中的setName()方法 -->
<property name="name" value="Spring"></property>
</bean>
②通过构造方法注入Bean的属性或依赖的对象,它保证了Bean的实例在实例化之后就可以使用,
构造器注入在<constructor-arg>元素里声明属性,<constructor-arg>中没有name属性
③通过调用静态工厂方法创建Bean
调用静态工厂方法创建 Bean是将对象创建的过程封装到静态方法中. 当客户端需要对象时, 只需要简单地调用静态方法, 而不同关心创建对象的细节,要声明通过静态方法创建的 Bean, 需要在 Bean 的 class 属性里指定拥有该工厂的方法的类, 同时在 factory-method 属性里指定工厂方法的名称. 最后, 使用 <constrctor-arg> 元素为该方法传递方法参数
class:指向工厂方法的全类名
factory-method:指向静态方法的名称
<constructor-arg>:元素如果工厂方法需要传入参数,则使用它来配置参数
<!-- 通过静态工厂方法来配置Bean,注意不是配置静态工厂方法实例,而是配置Bean实例 -->
<bean id="car2" class="com.test.spring.BeanFactory.StaticCarFactory"
factory-method="getCar">
<constructor-arg value="audi"></constructor-arg>
</bean> ④通过FactoryBean配置Bean
class:指向FactoryBean的全类名
property:配置factoryBean的属性
但实际返回的是实例确实FactoryBean的getObject()方法返回的实例
<bean id="car4" class="com.test.spring.BeanFactory.CarFactoryBean">
<property name="brand" value="BMW"></property>
</bean> ⑤配置引用其他的Bean
组成应用程序的Bean经常要相互协作以完成应用程序的功能,要是Bean能够互相访问,就必须在Bean的配置文件中指定对Bean应用
<ref>元素或ref属性:为Bean的属性和构造器参数指定对Bean的引用
⑥内部Bean:在属性和构造器中包含Bean的声明,内部Bean不能够被外部Bean引用配置级联属性 ⑦配置集合属性
在Spring中可以通过一组内置的的xml标签(例如:<list>、<set>、<map>)来配置集合属性。
List:配置java.uitl.List类型的元素属性,需要指定<list>标签,在标签里包含一些元素,这些标签可以通过
<value>指定简单的常量值,通过<ref>指定对其他Bean的引用,通过<Bean>指定内置的Bean定义,通过<null/>
指定空元素,甚至是内嵌其他集合。
<bean id="person3" class="com.test.spring.ListBeans.ListPersons">
<property name="name" value="Mike"></property>
<property name="age" value="22"></property>
<property name="cars">
<list>
<ref bean="car"/>
<ref bean="car2"/>
<ref bean="car3"/>
<!-- 也可以使用内部Bean -->
<bean class="com.test.spring.bean.Car">
<constructor-arg value="Audi"></constructor-arg>
<constructor-arg value="QQ"></constructor-arg>
<constructor-arg value="320000"></constructor-arg>
<constructor-arg value="220"></constructor-arg>
</bean>
</list>
</property>
</bean> set集合的配置类似List。 map集合:通过<map>标签定义,<map>标签中可以使用多个<entry>作为子标签,每个条目包含一堆键值对
必须有<key>标签里定义键,因为键和值的类型没有限制,可以*的为他们指定<value>、<ref>、<bean>、<null>元素。
可以将Map的键和值作为<entry>的属性定义:简单常量使用key和value来定义,Bean的引用使用<key-ref>
和<value-ref>属性定义,使用<props>定义java.util.Properties该标签使用多个<prop>作为子标签,
每个<prop>标签必须定义key属性。
<bean id="person4" class="com.test.spring.ListBeans.MapPersons">
<property name="name" value="Rose"></property>
<property name="age" value="30"></property>
<property name="cars">
<map>
<entry key="AA" value-ref="car"></entry>
<entry key="BB" value-ref="car2"></entry>
<!-- 也可以使用内部Bean -->
</map>
</property>
</bean> 配置Properties属性值:
<bean id="dataSource" class="com.test.spring.ListBeans.DataSource">
<property name="properties">
<props>
<prop key="user">root</prop>
<prop key="password">root</prop>
<prop key="jdbcUrl">jabc:mysql:///test</prop>
<prop key="driverClass">com.mysql.jabc.Driver</prop>
</props>
</property>
</bean>
使用utility scheme定义集合:使用基本的集合标签定义集合时,不能将集合作为独立的Bean定义,导致其他Bean
无法引用该集合,所以无法在不用的Bean之间共享集合,可以使用utility scheme里的集合标签定义独立的集合Bean
需要注意的是,必须在<Beans>根元素里添加util scheme定义。
<!-- ④引用集合Bean,配置一个单独的集合Bean,以供其他Bean引用,需要导入util命名空间 -->
<util:list id="cars">
<ref bean="car"/>
<ref bean="car2"/>
<ref bean="car3"/>
</util:list>
<!-- 引用以上的cars -->
<bean id="person5" class="com.test.spring.ListBeans.ListPersons">
<property name="name" value="Jack"></property>
<property name="age" value="23"></property>
<property name="cars" ref="cars"></property>
</bean> ⑧使用p命名空间,为了简化XML文件的配置,越来越多的XML文件采用属性而非子元素配置信息,Spring2.5版本开始
引用一个新的p命名空间,可以通过<Bean>元素属性的方式,配置Bean的属性,使用命名空间后,基于XML的配置方式将
进一步简化。
<bean id="person6" class="com.test.spring.ListBeans.ListPersons" p:name="Queen" p:age="32" p:cars-ref="cars">
</bean>

7.自动装配

Spring IOC容器可以自动装配Bean,需要做的仅仅是在的autowire属性里指定自动装配的模式

自动装配的模式:byType、byName

自动装配的缺点:在Bean配置文件里设置autowire属性进行自动装配将会装配Bena的所有属性,然而之希望装配个别属性

时,autowire属性就不那么灵活了,autowire属性要么根据类型自动装配,要么根据名称自动装配,一般情况下项目项目中

很少使用自动装配,因为和自动装配的好处比起来,清晰明确的配置文档更有说服力。

byType(根据类型自动装配):根据Bean的类型和当前Bean的属性的类型进行自动装配,若IOC容器中有多个与目标

Bean一致的Bean,这种情况下Spring将无法判断那个Bean才适合该属性,所以会导致不能执行自动装配。

byName(根据名称自动装配):根据bean的名字和当前Bean的setter风格的属性名进行自动装配,若有匹配的,

则进行自动装配,否则,不装配,必须将目标Bean的名称和属性设置的完全相同。

constructor(通过构造器自动装配):当Bean中存在多个构造器时,这种自动装配方式将会很复杂,不推荐使用。

8.Bean之间的关系

继承、依赖、抽象

Spring 允许继承 bean 的配置, 被继承的 bean 称为父 bean. 继承这个父 Bean 的 Bean 称为子 Bean

子 Bean 从父 Bean 中继承配置, 包括 Bean 的属性配置子 Bean 也可以覆盖从父 Bean 继承过来的配置,父 Bean

可以作为配置模板, 也可以作为 Bean 实例. 若只想把父 Bean 作为模板, 可以设置 的abstract 属性为 true,

这样 Spring 将不会实例化这个 Bean并不是 元素里的所有属性都会被继承. 比如: autowire, abstract 等.

也可以忽略父 Bean 的 class 属性, 让子 Bean 指定自己的类, 而共享相同的属性配置. 但此时 abstract 必须设为 true

依赖Bean配置,Spring用户允许用户通过的depends-on属性设定Bean的前置依赖Bean,前置依赖的bean会在本Bean

实例化之前创建好,如果前置依赖于多个Bean,则可以通过逗号,空格的方式配置前置Bean的名称

9.Bean的作用域

①singleton:IOC容器默认的作用域是单例的,也就是说IOC容器只会为一个Bean节点创建一个Bean对象,
每次调用getBean()方法只会返回同一个对象。
②prototype:每次向容器获取Bean时,IOC容器都会创建一个新的Bean对象,该对象在创建IOC容器对象时没有创建,
在调用getBean()方法时,才创建。
③web环境作用域:很少使用

10.使用外部属性文件

在配置文件里配置 Bean 时, 有时需要在 Bean 的配置里混入系统部署的细节信息(例如: 文件路径, 数据源配置信息等),

而这些部署细节实际上需要和 Bean 配置相分离Spring 提供了一个 PropertyPlaceholderConfigurer 的 BeanFactory

后置处理器, 这个处理器允许用户将 Bean 配置的部分内容外移到属性文件中. 可以在 Bean 配置文件里使用形式为 var的变量,PropertyPlaceholderConfigurer从属性文件里加载属性,并使用这些属性来替换变量。Spring还允许在属性文件中使用{propName},

以实现属性之间的相互引用。

在Spring2.0的时候,使用外部属性文件:

<bean class="org.springframework.bean.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.propertiess">
</bean>
Spring2.5之后:可通过<context:property-placehoder>元素简化
①:在<beans>中添加context Scheme定义
②:在配置文件中加入如下配置:
<context:property-placeholder location="classpath:db.properties"/>

11.SpEl:Spring表达式语言

①SpEl:是一个支持运行时查询和操作对象图的强大的表达式语言。

②语法类似于 EL:SpEL 使用 #{…} 作为定界符,所有在大框号中的字符都将被认为是 SpEL,SpEL 为 bean 的属性进行动态赋值提供了便利

③SpEL功能:

通过 bean 的 id 对 bean 进行引用

调用方法以及引用对象中的属性

计算表达式的值

正则表达式的匹配

④为属性赋值

整数:<property name="count" value="#{5}"/>
小数:<property name="frequency" value="#{89.7}"/>
科学计数法:<property name="capacity" value="#{1e4}"/>
String可以使用单引号或者双引号作为字符串的定界符号:<property name=“name” value="#{'Chuck'}"/>
或 <property name='name' value='#{"Chuck"}'/>
Boolean:<property name="enabled" value="#{false}"/>

⑤引用Bean、属性和方法

引用其他对象(通过value属性和SpEl配置Bean之间的应用关系)

    <property name="prefix" value="#{prefixGenerator}">
引用其他对象的属性(通过value属性和SpEl配置suffix属性值为另外一个Bean的suffix属性值)
<property name="prefix" value="#{prefixGenerator.suffix}">
调用其他方法,还可以链式操作(通过value属性和SpEl配置suffix属性值为另外一个Bean的方法的返回值 )
<property name="suffix" value="#{sequenceGenerator2.toString()}"/>
方法的连缀
<property name="suffix" value="#{sequenceGenerator2.toString().toUpperCase()}"/>
调用静态方法或静态属性
<property name="initValue" value="#{T(java.lang.Math).PI}"></property>

⑥支持的运算符

+、-、*、/、%、^、+(做字符串连接符)、<、>、==、<=、>=、lt、gt、eg、le、ge、and、or、not、if、else

正则表达式

12.IOC容器中Bean的生命周期

SpringIOC容器可以管理Bean的生命周期,Spring允许在Bean的生命周期的特定点执行定制的任务。

SpringIOC容器对Bean的生命周期进行管理的过程

①通过构造器或工厂方法创建Bean实例
②为Bean的属性设置值和对其他Bean的引用
③调用Bean的初始化方法
④Bean可以使用了
⑤当容器关闭时,调用Bean的销毁方法
在Bean的声明里设置init-method和destory-method属性,为Bean指定初始化和销毁的方法

13.以上配置Bean使用的都是基于XML文件的方式,同时也可以使用基于注解的方式:基于注解配置Bean、基于注解来装配Bean的属性

    在 classpath 中扫描组件:
组件扫描(component scanning): Spring 能够从 classpath 下自动扫描, 侦测和实例化具有特定注解的组件.
特定组件包括:
@Component: 基本注解, 标识了一个受 Spring 管理的组件
@Respository: 标识持久层组件(要么添加到接口上,要么添加到接口的实现类上)
@Service: 标识服务层(业务层)组件
@Controller: 标识表现层组件
对于扫描到的组件, Spring 有默认的命名策略: 使用非限定类名, 第一个字母小写. 也可以在注解中通过 value 属性值标识组件的名称
当在组件类上使用了特定的注解之后, 还需要在 Spring 的配置文件中声明 <context:component-scan> :
base-package 属性指定一个需要扫描的基类包,Spring 容器将会扫描这个基类包里及其子包中的所有类.
当需要扫描多个包时, 可以使用逗号分隔.
如果仅希望扫描特定的类而非基包下的所有类,可使用 resource-pattern 属性过滤特定的类,示例:
<context:component-scan
base-package="com.test.web"
resource-pattern="autowire/*.calss"
<context:include-filter> 子节点表示要包含的目标类
<context:exclude-filter> 子节点表示要排除在外的目标类
<context:component-scan> 下可以拥有若干个 <context:include-filter> 和 <context:exclude-filter> 子节点 @Autowired 注解自动装配具有兼容类型的单个 Bean属性
构造器, 普通字段(即使是非 public), 一切具有参数的方法都可以应用@Authwired 注解
默认情况下, 所有使用 @Authwired 注解的属性都需要被设置(注解标识). 当 Spring 找不到匹配的 Bean 装配属性时, 会抛出异常,
若某一属性允许不被设置, 可以设置 @Authwired 注解的 required 属性为 false,默认情况下, 当 IOC 容器里存在多个
类型兼容的 Bean 时,通过类型的自动装配将无法工作. 此时可以在 @Qualifier 注解里提供 Bean 的名称. Spring 允许对方法的入参
标注 @Qualifiter 已指定注入 Bean 的名称
@Authwired 注解也可以应用在数组类型的属性上, 此时 Spring 将会把所有匹配的 Bean 进行自动装配.
@Authwired 注解也可以应用在集合属性上, 此时 Spring 读取该集合的类型信息, 然后自动装配所有与之兼容的 Bean.
@Authwired 注解用在 java.util.Map 上时, 若该 Map 的键值为 String, 那么 Spring 将自动装配与之 Map 值类型
兼容的 Bean, 此时 Bean 的名称作为键值

总结:

    ①使用@Component、@Respository、@Service、@Controller注解类。
②在配置文件中<!-- 指定springIOC容器扫描的包 -->。
<context:component-scan base-package="com.test.spring.beanAnnotation"></context:component-scan>
③使用@Authwired注解为属性字段自动装配Bean,该字段一定要先执行第一步。

问题:当存在类型一样的Bean时,例如实现了同一个接口的两个实现类,则他们的类型一致,如何解决自定装配的问题?

    解决方案一:这是就需要在注解中设置value的属性,该属性值与@Authwired注解标注的字段一样
例如:@Autowired
private UserRepository userRepository;
//UserRepository的实现类上的注解value值为字段的值
@Repository(value="userRepository")
public class UserRepositoryImpl implements UserRepository{}
解决方案二:使用@Qualifier("userJDBCRepository")标注Bean的名称,该注解甚至可以对方法的入参标注
例如:@Qualifier("userJDBCRepository")
private UserRepository userRepository;
该属性会自动装配名称为@Qualifier注解value属性值的Bean

这两种方案一种是在注解字段上做手脚(设置value值),一种是在注解类上做手脚(设置value值)

14.泛型依赖注入

Spring 4.x中可以为子类注入子类对应的泛型类型的成员变量的引用,父类之间建立引用关系

二、Spring案例HelloWorld

1.添加jar包到classpath下

commons-logging.jar

spring-beans.jar

spring-context.jar

spring-core.jar

spring-expression.jar

2.Spring配置文件:一个典型的Spring项目需要创建一个或多个bean配置文件,这些配置文件用于在SpringIOC容器里

配置Bean,Bean配置文件可以放在classpath下,也可以放在其他目录下