Spring(2)—IOC

一、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 配置文件里使用形式为 varPropertyPlaceholderConfigurer,使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下,也可以放在其他目录下

原文地址:https://www.cnblogs.com/tengpengfei/p/10453969.html