zoukankan      html  css  js  c++  java
  • IoC/DI开发

    IoC——Inversion of Control,控制反转
    在Java开发中,IoC意味着将你设计好的类交给系统去控制,而不是在你的类内部控制。IoC是一种让服务消费者不直接依赖于服务提供者的组件设计方式,是一种减少类与类之间依赖的设计原则。
     
    DI——Dependency Injection(依赖注入
    即组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中。
    依赖注入的目标并非为软件系统带来更多的功能,而是为了提升组件重用的概率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务 逻辑,而不用关心具体的资源来自何处、由谁实现。
    1:控制反转:
    谁控制谁?控制什么?为何叫反转(对应于正向)?哪些方面反转了 ?为何需要反转?
    2:依赖:
    什么是依赖(按名词理解,按动词理解)?谁依赖于谁?为什么需要依赖?依赖什么东西?
    3:注入:
    谁注入于谁?注入什么东西?为何要注入?
    4:依赖注入和控制反转是同一概念吗?
    5:参与者都有哪些?
    6:IoC/DI是什么?能做什么?怎么做?用在什么地方?
     
    还不能完全回答和理解,没有关系,先来看看IoC/DI的基本思想演变,然后再回头来回答这些问题
     
     
    IoC容器
    简单的理解就是:实现IoC思想,并提供对象创建、对象装配以及对象生命周期管理的软件就是IoC容器。
    IoC理解
    1:应用程序无需主动new对象;而是描述对象应该如何被创建即可
    IoC容器帮你创建,即被动实例化;
    2:应用程序不需要主动装配对象之间的依赖关系,而是描述需要哪个服务
    IoC容器会帮你装配(即负责将它们关联在一起),被动接受装配;
    3:主动变被动,体现好莱坞法则:别打电话给我们,我们会打给你
    4:体现迪米特法则(最少知识原则):应用程序不知道依赖的具体实现,只知道需要提供某类服务的对象(面向接口编程);并松散耦合,一个对象应当对其他对象有尽可能少的了解,不和陌生人(实现)说话
    5:是一种让服务消费者不直接依赖于服务提供者的组件设计方式,是一种减少类与类之间依赖的设计原则。
     
    使用IoC/DI容器开发需要改变的思路
    1:应用程序不主动创建对象,但要描述创建它们的方式。
    2:在应用程序代码中不直接进行服务的装配,但要描述哪一个组件需要哪一项服务,由容器负责将这些装配在一起。
     
    也就是说:所有的组件都是被动的,组件初始化和装配都由容器负责,应用程序只是在获取相应的组件后,实现应用的功能即可。
     
    提醒一点
    IoC/DI是思想,不是纯实现技术。IoC是框架共性,只是控制权的转移,转移到框架,所以不能因为实现了IoC就叫IoC容器,而一般除了实现了IoC外,还具有DI功能的才叫IoC容器,因为容器除了要负责创建并装配组件关系,还需要管理组件生命周期。
    n工具准备
    1:Eclipse + Jdk6.0 ,示例用的Eclipse是Eclipse Java EE IDE for Web Developers,Version: Helios Service Release 1
    2:spring-framework-3.1.0.M2-with-docs.zip
     
    构建环境
    1:在Eclipse里面新建一个工程,设若名称是Spring3test
    2:把发行包里面的dist下面的jar包都添加到Eclipse里面
    3:根据Spring的工程来获取Spring需要的依赖包,在联网的情况下,通过Ant运行projects/build-spring-framework/build.xml,会自动去下载所需要的jar包,下载后的包位于projects/ivy-cache/repository下面。
    4:为了方便,把这些jar包也添加到Eclipse里面
    开发接口

    java代码:
    public interface HelloApi {
    public String helloSpring3(int a); 
    }
    开发实现类

    java代码:
    public class HelloImpl implements HelloApi{
    public String helloSpring3(int a){
    System.out.println("hello Spring3==="+a);
    return "Ok,a="+a;
    }
    }
    配置文件
    1:在src下面新建一个文件叫applicationContext.xml
    2:在Spring发行包里面搜索一个例子,比如使用:projects\org.springframework.context\src\test\java\org\springframework\jmx下的applicationContext.xml,先把里面的配置都删掉,留下基本的xml定义和根元素就可以了,它是一个DTD版的,而且还是2.0版的。

    java代码:
    3:建议使用Spring3的Schema版本,示例如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    ">
    ………………………
    </beans>
    4:配置applicationContext.xml如下:

    java代码:
    <bean name="helloBean" class="cn.javass.Spring3.hello.HelloImpl"></bean>
     
    编写客户端如下:

    java代码:
    package cn.javass.Spring3.hello;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    public class Client {
    public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext(
            new String[] {"applicationContext.xml"});
    HelloApi api = (HelloApi)context.getBean("helloBean");
    String s = api.helloSpring3(3);
    System.out.println("the s="+s);
    }
    }
    审视和结论
    1:所有代码中(除测试代码之外),并没有出现Spring的任何组件 。
    2:客户代码(这里就是我们的测试代码)仅仅面向接口编程,而无需知道实现类的具体名称。同时,我们可以很简单的通过修改配置文件来切换具体的底层实现类 。
     
    结论
    1:首先,我们的组件并不需要实现框架指定的接口,因此可以轻松的将组件从Spring脱离,甚至不需要任何修改(这在基于EJB架实现的应用中是难以想象的)。
    2:其次,组件间的依赖关系减少,极大改善了代码的可重用性和可维护性
    3:面向接口编程
     
    什么是Spring中的Bean
    在Spring中,那些组成应用的主体及由Spring IoC容器所管理的对象被称之为bean。简单地讲,bean就是由Spring容器初始化、装配及被管理的对象,除此之外,bean就没有特别之处了(与应用中的其他对象没有什么区别)。而bean定义以及bean相互间的依赖关系将通过配置元数据来描述。
    为什么使用Bean这个名字
    使用‘bean’这个名字而不是‘组件’(component) 或‘对象’(object)的动机源于Spring框架本身(部分原因则是相对于复杂的EJB而言的)。
    Spring的IoC容器
    org.springframework.beans.factory.BeanFactory是Spring IoC容器的实际代表者,IoC容器负责容纳bean,并对bean进行管理。
    Spring IoC容器将读取配置元数据;并通过它对应用中各个对象进行实例化、配置以及组装。通常情况下我们使用简单直观的XML来作为配置元数据的描述格式。在XML配置元数据中我们可以对那些我们希望通过Spring IoC容器管理的bean进行定义。
    IoC/DI是Spring最核心的功能之一, Spring框架所提供的众多功能之所以能成为一个整体正是建立在IoC的基础之上
    BeanFactory和ApplicationContext
    org.springframework.beans及org.springframework.context包是Spring IoC容器的基础。BeanFactory提供的高级配置机制,使得管理任何性质的对象成为可能。 ApplicationContext是BeanFactory的扩展,功能得到了进一步增强,比如更易与Spring AOP集成、消息资源处理(国际化处理)、事件传递及各种不同应用层的context实现(如针对web应用的WebApplicationContext)。
    接口选择之惑
    在实际应用中,用户有时候不知道到底是选择BeanFactory接口还是ApplicationContext接口。但是通常在构建JavaEE应用时,使用ApplicationContext将是更好的选择,因为它不仅提供了BeanFactory的所有特性,同时也允许使用更多的声明方式来得到我们想要的功能。
    简而言之,BeanFactory提供了配制框架及基本功能,而ApplicationContext则增加了更 多支持企业核心内容的功能。ApplicationContext完全由BeanFactory扩展而来,因而BeanFactory所具备的能力和行为也适用于ApplicationContext。
    Spring IoC容器的实例化非常简单,如下面的例子:
    1:第一种:

    java代码:
    Resource resource = new FileSystemResource("beans.xml");
    BeanFactory factory = new XmlBeanFactory(resource);
     
    2:第二种

    java代码:
    ClassPathResource resource = new ClassPathResource("beans.xml");
    BeanFactory factory = new XmlBeanFactory(resource);
     
    3:第三种:

    java代码:
    ApplicationContext context = new ClassPathXmlApplicationContext(
            new String[] {"applicationContext.xml", "applicationContext-part2.xml"});
    // of course, an ApplicationContext is just a BeanFactory
    BeanFactory factory = (BeanFactory) context;
    读取多个配置文件
    第一种方法:
    为了加载多个XML文件生成一个ApplicationContext实例,可以将文件路径作为字符串数组传给ApplicationContext构造器。而bean factory将通过调用bean defintion reader从多个文件中读取bean定义。通常情况下,Spring团队倾向于上述做法,因为这样各个配置并不会查觉到它们与其他配置文件的组合。
    第二种方法:
    使用一个或多个的<import/>元素来从另外一个或多个文件加载bean定义。所有的<import/>元素必须放在<bean/>元素之前以完成bean定义的导入。 让我们看个例子:

    java代码:
    <beans><import resource="services.xml"/>
        <import resource=“/resources/messageSource.xml"/>
        <import resource="/resources/themeSource.xml"/>
          <bean id="bean1" class="..."/>
        <bean id="bean2" class="..."/>
      </beans>
     
    配置文件中常见的配置内容
    在IoC容器内部,bean的定义由BeanDefinition 对象来表示,该定义将包含以下信息:
    1:全限定类名:这通常就是已定义bean的实际实现类。如果通过调用static factory方法来实例化bean,而不是使用常规的构造器,那么类名称实际上就是工厂类的类名。
    2:bean行为的定义,即创建模式(prototype还是singleton)、自动装配模式、依赖检查模式、初始化以及销毁方法。这些定义将决定bean在容器中的行为。
    3:用于创建bean实例的构造器参数及属性值。比如使用bean来定义连接池,可以通过属性或者构造参数指定连接数,以及连接池大小限制等。
    4:bean之间的关系,即协作 (或者称依赖)。
    Bean的命名
    每个bean都有一个或多个id(或称之为标识符或名称,在术语上可以理解成一回事),这些id在当前IoC容器中必须唯一。
    当然也可以为每个bean定义一个name,但是并不是必须的,如果没有指定,那么容器将为其生成一个惟一的name。对于不指定name属性的原因我们会在后面介绍(比如内部bean就不需要)。
    Bean命名的约定
    bean的命名采用标准的Java命名约定,即小写字母开头,首字母大写间隔的命名方式。如accountManager、 accountService等等。
    对bean采用统一的命名约定将会使配置更加简单易懂。而且在使用Spring AOP,这种简单的命名方式将会令你受益匪浅。
    Bean的别名
    一个Bean要提供多个名称,可以通过alias属性来加以指定 ,示例如下:
    <alias name="fromName" alias="toName"/>
    容器如何实例化Bean
    当采用XML描述配置元数据时,将通过<bean/>元素的class属性来指定实例化对象的类型。class属性主要有两种用途:在大多数情况下,容器将直接通过 反射调 指定类的构造器来创建bean(这有点等类似于在Java代码中使用new操作符);在极少数情况下,容器将调用类的静态工厂方法来创建bean实例,class属性将用来指定实际具有静态工厂方法的类(至于调用静态工厂方法创建的对象类型是当前class还是其他的class则无关紧要)。
    用构造器来实例化Bean ,前面的实例就是
    使用静态工厂方法实例化
    采用静态工厂方法创建bean时,除了需要指定class属性外,还需要通过factory-method属性来指定创建bean实例的工厂方法,示例如下:
    <bean id="exampleBean"
          class="examples.ExampleBean2"
          factory-method="createInstance"/>
    使用实例工厂方法实例化
    使用此机制,class属性必须为空,而factory-bean属性必须指定为当前(或其祖先)容器中包含工厂方法的bean的名称,而该工厂bean的工厂方法本身必须通过factory-method属性来设定,并且这个方法不能是静态的,示例如下:
    <bean id="exampleBean" factory-bean="myFactoryBean" factory-method="createInstance"/>
     
    使用容器
    从本质上讲,BeanFactory仅仅只是一个维护bean定义以及相互依赖关系的高级工厂接口。使用getBean(String)方法就可以取得bean的实例;BeanFactory提供的方法极其简单。n依赖注入(DI) 背后的基本原理
    是对象之间的依赖关系(即一起工作的其它对象)只会通过以下几种方式来实现: 构造器的参数、 工厂方法的参数,或 给由构造函数或者工厂方法创建的对象设 置属性
    因此,容器的工作就是创建bean时注入那些依赖关系。相对于由bean自己来控制其实例化、直接在构造器中指定依赖关系或则类似服务定位器(Service Locator)模式这3种自主控制依赖关系注入的方法来说,控制从根本上发生了倒转,这也正是控制反转IoC名字的由来。
    应用依赖注入(DI)的好处、
    应用DI原则后,代码将更加清晰。而且当bean自己不再担心对象之间的依赖关系(以及在何时何地指定这种依赖关系和依赖的实际类是什么)之后,实现更高层次的 松耦合将易如反掌。
    依赖注入(DI)基本的实现方式
    DI主要有两种注入方式,即 Setter注入和 构造器注入
    通过调用无参构造器或无参static工厂方法实例化bean之后,调用该bean的setter方法,即可实现基于setter的DI。 示例如下:
    示例——Java类

    java代码:
    public class HelloImpl implements HelloApi{
    private String name = "";
    public void setName(String name){
    this.name = name;
    }
    public String helloSpring3(int a){
    System.out.println("hello Spring3==="+a+",name="+name);
    return "Ok,a="+a;
    }
    }
    
    示例——配置文件
    <bean name="helloBean" class="cn.javass.Spring3.hello.HelloImpl">
    <property name="name"><value>javass Spring3</value></property>
    </bean>
    示例——Java类

    java代码:
    public class HelloImpl implements HelloApi{
    private String name = "";
    public HelloImpl(String name){
    this.name = name;
    }
    public String helloSpring3(int a){
    System.out.println("hello Spring3==="+a+",name="+name);
    return "Ok,a="+a;
    }
    }
    示例——配置文件

    java代码:
    <bean name="helloBean" class="cn.javass.Spring3.hello.HelloImpl">
    <constructor-arg><value>javass Spring3</value></constructor-arg>
    </bean>
    默认解析方式
    构造器参数将根据类型来进行匹配。如果bean定义中的构造器参数类型明确,那么bean定义中的参数顺序就是对应构造器参数的顺序
    构造器参数类型匹配
    可以使用type属性来显式的指定参数所对应的简单类型。例如:

    java代码:
    <bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
    </bean>
    构造器参数的索引
    使用index属性可以显式的指定构造器参数出现顺序。例如:

    java代码:
    <bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
    <constructor-arg index="1" value="42"/>
    </bean>
    构造器参数的名称
    在Spring3里面,可以使用构造器参数的名称来直接赋值。例如:

    java代码:
    <bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateanswer" value="42"/>
    </bean>
    直接量(基本类型、Strings类型等)
    <value/>元素通过字符串来指定属性或构造器参数的值。JavaBean属性编辑器将把字符串从java.lang.String类型转化为实际的属性或参数类型。示例:

    java代码:
    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName">
    <value>oracle.jdbc.driver.OracleDriver</value>
    </property>
    <property name="url">
    <value>jdbc:oracle:thin:@localhost:1521:orcl</value>
    </property>
    <property name="username"> <value>test</value> </property>
    <property name="password" value=“test"/>
    </bean>
    Value可以做为子元素或者是属性使用。
    nidref元素
    idref元素用来将容器内其它bean的id传给<constructor-arg/> 或 <property/>元素,同时提供错误验证功能。 idref元素和<value>差不多,只是传递 一个字符串,用来方便xml检查。示例如下:

    java代码:
    <bean id="theTargetBean" class="..."/>
    <bean id="theClientBean" class="...">
    <property name="targetName"> <idref bean="theTargetBean" /> </property>
    </bean>
    上述bean定义片段完全地等同于(在运行时)以下的片段
    <bean id="theTargetBean" class="..."/>
    <bean id="client" class="...">
    <property name="targetName"> <value>theTargetBean</value> </property>
    </bean>
    idref元素 续
    第一种形式比第二种更可取的主要原因是,使用idref标记允许容器在部署时 验证所被引用的bean是否存在。而第二种方式中,传给client bean的targetName属性值并没有被验证。任何的输入错误仅在client bean实际实例化时才会被发现(可能伴随着致命的错误)。如果client bean 是prototype类型的bean,则此输入错误(及由此导致的异常)可能在容器部署很久以后才会被发现。
    如果被引用的bean在同一XML文件内,且bean名字就是bean id,那么可以使用local属性,此属性允许XML解析器在解析XML文件时来对引用的bean进行验证 ,示例如下:
    <property name="targetName">
    <idref local="theTargetBean"/>
    </property>
    引用其它的bean(协作者) ——ref元素
    尽管都是对另外一个对象的引用,但是通过id/name指向另外一个对象却有三种不同的形式,不同的形式将决定如何处理作用域及验证。
    1:第一种形式也是最常见的形式是使用<ref/>标记指定目标bean,示例:
    <ref bean=“someBean”/> 
    2:第二种形式是使用ref的local属性指定目标bean,它可以利用XML解析器来验证所引用的bean是否存在同一文件中。示例:
    <ref local="someBean"/>
    3:第三种方式是通过使用ref的parent属性来引用当前容器的父容器中的bean,并不常用。示例:

    java代码:
    <bean id="accountService" class="com.foo.SimpleAccountService"> </bean>
    <bean id=“accountService” <-- 注意这里的名字和parent的名字是一样的--> class="org.springframework.aop.framework.ProxyFactoryBean">
     <property name="target"><ref parent="accountService"/> </property>
    </bean>
    内部Bean
    所谓的内部bean(inner bean)是指在一个bean的<property/>或 <constructor-arg/>元素中使用<bean/>元素定义的bean。内部bean定义不需要有id或name属性,即使指定id 或 name属性值也将会被容器忽略。示例:

    java代码:
    <bean id="outer" class="...">
    <property name="target">
    <bean class="com.mycompany.Person">
    <property name="name" value="Fiona Apple"/>
    <property name="age" value="25"/>
    </bean>
    </property>
    </bean>
    通过<list/>、<set/>、<map/>及<props/>元素可以定义和设置与Java Collection类型对应List、Set、Map及Properties的值 ,示例如下:

    java代码:
    1. <bean id="moreComplexObject" class="example.ComplexObject">  
    2. <property name="adminEmails">  
    3. <props>  
    4. <prop key="administrator">admin@somecompany.org</prop>  
    5. <prop key="support">support@somecompany.org</prop>  
    6. </props>  
    7. </property>  
    8. <property name="someList">  
    9. <list>  
    10. <value>a list element followed by a reference</value>  
    11. <ref bean="myDataSource" />  
    12. </list>  
    13. </property>  
    14. <property name="someMap">  
    15. <map>  
    16. <entry>  
    17. <key> <value>yup an entry</value> </key>  
    18. <value>just some string</value>  
    19. </entry>  
    20. <entry>  
    21. <key> <value>yup a ref</value> </key>  
    22. <ref bean="myDataSource" />  
    23. </entry>  
    24. </map>  
    25. </property>  
    26. <property name="someSet">  
    27. <set>  
    28. <value>just some string</value>  
    29. <ref bean="myDataSource" />  
    30. </set>  
    31. </property>  
    32. </bean>  
    可以定义parent-style和child-style的<list/>、<map/>、<set/>或<props/>元素,子集合的值从其父集合继承和覆盖而来;也就是说,父子集合元素合并后的值就是子集合中的最终结果,而且子集合中的元素值将覆盖父集全中对应的值。

    java代码:
    1. <beans>  
    2. <bean id="parent" abstract="true" class="example.ComplexObject">  
    3.     <property name="adminEmails">  
    4.         <props>  
    5.             <prop key="administrator">administrator@somecompany.com</prop>  
    6.             <prop key="support">support@somecompany.com</prop>  
    7.         </props>  
    8.     </property>  
    9. </bean>  
    10. <bean id="child" parent="parent" class" example.Child" >  
    11.     <property name="adminEmails">  
    12.         <props merge="true">  
    13.             <prop key="sales">sales@somecompany.com</prop>  
    14.             <prop key="support">support@somecompany.co.uk</prop>  
    15.         </props>  
    16.     </property>  
    17. </bean>  
    18. <beans>  
    在上面的例子中,childbean的adminEmails属性的<props/>元素上使用了merge=true属性。当child bean被容器实际解析及实例化时,其 adminEmails将与父集合的adminEmails属性进行合并 。
    注意到这里子bean的Properties集合将从父<props/>继承所有属性元素。同时子bean的support值将覆盖父集合的相应值
    不同的集合类型是不能合并(如map和 list是不能合并的),否则将会抛出相应的Exception。merge属性必须在继承的子bean中定义,而在父bean的集合属性上指定的merge属性将被忽略
    在JDK5以上的版本里,Spring支持强类型集合
    <null/>用于处理null值,Spring会把属性的空参数当作空字符串处理。
    1:以下的xml片断将email属性设为空字符串。
    <bean class="ExampleBean">
      <property name="email"><value></value></property>
    </bean>
    这等同于Java代码: exampleBean.setEmail(“”)。
    2:而null值则可以使用<null>元素可用来表示。例如:
    <bean class="ExampleBean">
      <property name="email"><null/></property>
    </bean>
    上述的配置等同于Java代码:exampleBean.setEmail(null)。
    针对常见的value值或bean的引用,Spring提供了简化格式用于替代<value/>和<ref/>元素 。如下:

    java代码:
    1. <property name="myProperty">  
    2.   <value>hello</value>  
    3. </property>  
    4. <property name="myProperty">  
    5.   <ref bean="myBean">  
    6. </property>  
    7. <entry>  
    8.   <key>  
    9.     <ref bean="myKeyBean" />  
    10.   </key>  
    11.   <ref bean="myValueBean" />  
    12. </entry>  
    完全等同于
    <property name="myProperty" value="hello"/>
    <property name="myProperty" ref="myBean"/>
    <entry key-ref="myKeyBean" value-ref="myValueBean"/>
     
    强调一点
    只有<ref bean=“xxx”>元素的简写形式,没有<ref local=“xxx”>的简写形式。也就是说<property name=“myProperty” ref=“myBean”/> 里面的ref是相当于<ref bean=“”>的形式。
    组合属性名称
    当设置bean的组合属性时,除了最后一个属性外,只要其他属性值不为null,组合或嵌套属性名是完全合法的。例如,下面bean的定义:

    java代码:
    1. <bean id="foo" class="foo.Bar">  
    2.   <property name="fred.bob.sammy" value="123" />  
    3. </bean>  
    4. 表示foo   

    bean有个fred属性,此属性有个bob属性,而bob属性又有个sammy属性,最后把sammy属性设置为123。为了让此定义能工作, foo的fred属性及fred的bob属性在bean被构造后都必须非空,否则将抛出NullPointerException异常。
    depends-on,用于当前bean初始化之前显式地强制一个或多个bean被初始化。
    示例:

    java代码:
    1. <bean id="beanOne" class="ExampleBean" depends-on="manager"/>  
    2. <bean id="manager" class="ManagerBean" />  
    3.    
    若需要表达对多个bean的依赖,可以在'depends-on'中将指定的多个bean名字用分隔符进行分隔,分隔符可以是逗号、空格及分号等。下面的例子中使用了'depends-on'来表达对多个bean的依赖。
     

    java代码:
    1. <bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">  
    2.   <property name="manager" ref="manager" />  
    3. </bean>  
    4.   <bean id="manager" class="ManagerBean" />  
    5. <bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />  
    延迟初始化bean
    1:ApplicationContext实现的默认行为就是在启动时将所有singleton bean提前进行实例化,这样可能会增大资源的消耗,但会加快程序的运行速度。
     
    2:可以将bean设置为延迟实例化。在XML配置文件中,延迟初始化将通过<bean/>元素中的lazy-init属性来进行控制。例如:
    <bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true">
    </bean>
     
    3:在容器层次中通过在<beans/>元素上使用'default-lazy-init'属性来控制延迟初始化也是可能的。如下面的配置:
    <beans default-lazy-init="true">
    </beans>
     
    自动装配的优缺点
    1:优点:
    (1)自动装配能显著减少配置的数量。
    (2)自动装配可以使配置与java代码同步更新。例如,如果你需要给一个java类增加一个依赖,那么该依赖将被自动实现而不需要修改配置。
    2:缺点:
    (1)尽管自动装配比显式装配更神奇,但是,正如上面所提到的,Spring会尽量避免在装配不明确的时候进行猜测,因为装配不明确可能出现难以预料的结果,而且Spring所管理的对象之间的关联关系也不再能清晰的进行文档化。
    (2)对于那些根据Spring配置文件生成文档的工具来说,自动装配将会使这些工具没法生成依赖信息。
    (3)当根据类型进行自动装配的时候,容器中可能存在多个bean定义跟自动装配的setter方法和构造器参数类型匹配。虽然对于数组、集合以及Map,不存在这个问题,但是对于单值依赖来说,就会存在模棱两可的问题。如果bean定义不唯一,装配时就会抛出异常
    将bean排除在自动装配之外
    1:<bean/>元素的 autowire-candidate属性可被设为false,这样容器在查找自动装配对象时将不考虑该bean。
    2:另一个做法就是使用对bean名字进行模式匹配来对自动装配进行限制。其做法是在<beans/>元素的‘default-autowire-candidates’属性中进行设置。
    比如,将自动装配限制在名字以 'Repository'结尾的bean,那么可以设置为"*Repository“。对于多个匹配模式则可以使用逗号进行分隔。注意,如果在bean定义中的'autowire-candidate'属性显式的设置为'true' 或 'false',那么该容器在自动装配的时候优先采用该属性的设置,而模式匹配将不起作用。
    singleton
    在每个Spring IoC容器中一个bean定义对应一个对象实例,在读取配置文件创建IoC容器的时候就会根据配置初始化singleton的Bean实例
    prototype
    一个bean定义对应多个对象实例
    request
    在一次HTTP请求中,一个bean定义对应一个实例;即每次HTTP请求将会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。
    session
    在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
    global session
    在一个全局的HTTP Session中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext情形下有效。
     
      
    request、session以及global session仅在基于web的应用中使用
    初始化web配置,Servlet 2.4及以上的web容器,如下配置:

    java代码:
    1. <web-app>  
    2.   <listener>  
    3.     <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>  
    4.   </listener>  
    5. </web-app>  
    request作用域说明
    等同于Http的Request
    session作用域说明
    针对某个HTTP Session,Spring容器会根据bean定义创建一个全新的bean实例,且该bean仅在当前HTTP Session内有效。与request作用域一样,你可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉
    global session
    等同于标准的HTTP Session
    问题:比如在调用一个singleton类型bean A的某个方法时,需要引用另一个非singleton(prototype)类型的bean B,对于bean A来说,容器只会创建一次,这样就没法在需要的时候每次让容器为bean A提供一个新的的bean B实例 
    解决方案:Lookup方法注入,示例如下:

    java代码:
    1. Java类  
    2. public abstract class HelloImpl implements HelloApi{  
    3. private T2 t2;  
    4. public String helloSpring3(int a){  
    5. getT2().t1();  
    6. System.out.println("hello Spring3==="+a);  
    7. return "Ok,a="+a;  
    8. }  
    9. public abstract T2 getT2();  
    10. }  
    11. public class T2 {  
    12. public void t1(){  
    13. System.out.println("now in t1");  
    14. }  
    15. }  
    配置文件:

    java代码:
    1. <bean id="helloBean" class="cn.javass.Spring3.hello.HelloImpl">  
    2. <lookup-method name="getT2" bean="t2"/>  
    3. </bean>  
    4. <bean id="t2" class="cn.javass.Spring3.hello.T2"></bean>  
    5.    
    Lookup方法注入的内部机制是Spring利用了CGLIB库在运行时生成二进制代码功能,通过动态创建Lookup方法bean的子类而达到复写Lookup方法的目的
    初始化回调
    有两种方法,如下
    1:实现org.springframework.beans.factory.InitializingBean接口,这种方法不被推荐,因为这样和Spring耦合起来了。可以采用声明式的方法,如下:
    2:在Bean定义中指定一个普通的初始化方法,即在XML配置文件中通过指定init-method属性来完成,配置如下:
    <bean id="initBean" class="examples.ExampleBean" init-method="init"/>
    析构回调
    也有两种方法,如下
    1:实现org.springframework.beans.factory.DisposableBean接口,这种方法不被推荐,因为这样和Spring耦合起来了。可以采用声明式的方法,如下:
    <bean id="initBean" class="ex.ExampleBean" destroy-method="cleanup"/>
    缺省的初始化和析构方法
    配置在beans上,这样就不用每个Bean都配了,如下:
    <beans default-init-method="init">
    context包的核心是ApplicationContext接口。它由BeanFactory接口派生而来,除了提供了BeanFactory所有的功能,还提供了以下的功能:
    1:MessageSource, 提供国际化的消息访问
    2:资源访问,如URL和文件
    3:事件传播,实现了ApplicationListener接口的bean
    4:载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层
    利用MessageSource实现国际化
    1:在配置文件中添加

    java代码:
    1. <bean id="messageSource"        class="org.springframework.context.support.ResourceBundleMessageSource">  
    2.     <property name="basenames">  
    3.       <list>  
    4.         <value>format</value>  
    5.         <value>exceptions</value>  
    6.       </list>  
    7.     </property>  
    8.   </bean>  
    2:说明:上述配置是表示在classpath下有两个标准的properties文件。文件格式是标准的properties格式,Spring通过ResourceBundle,使用JDK中解析消息的标准方式,来处理任何解析消息的请求。
    3:测试代码:
    String msg = context.getMessage("testmsg", null, "Default", Locale.CHINA);
    前面getmessage方法的第二个参数是用来从程序中向消息里面传值的,如下:

    java代码:
    1. 消息文件:testmsg=this is a test,{0},{1}  
    2. Java类:String msg = context.getMessage("testmsg",  
    3. ew Object[]{"M1","M2"},"Default", Locale.CHINA);  
    4. System.out.println("msg="+msg);  
    前面getmessage方法的第四个参数是用来指定Locale的
    对于国际化(i18n),Spring中不同的MessageResource实现与JDK标准ResourceBundle中的locale解析规则一样。比如在上面例子中定义的messageSource bean,如果你想解析British (en-GB) locale的消息,那么需要创建format_en_GB.properties的资源文件;中国的如msg_zh_CN.properties。Locale解析通常由应用程序根据运行环境来指定。
    也可以把MessageSource当作资源注入到Bean中,Java类示例如下:

    java代码:
    1. public  class HelloImpl implements HelloApi{  
    2. private MessageSource ms = null;  
    3. public void setMs(MessageSource ms){  
    4. this.ms = ms;  
    5. }  
    6. public String helloSpring3(int a){  
    7. String msg = this.ms.getMessage("testmsg"null"Default", Locale.CHINA);  
    8. System.out.println("hello Spring3==="+msg);  
    9. return "Ok,a="+a;  
    10. }  
    11. }  
    配置文件示例如下:

    java代码:
    1. <bean id="helloBean" class="cn.javass.Spring3.hello.HelloImpl">  
    2. <property name="ms" ref="messageSource"></property>  
    3. </bean>  
    事件传播
    ApplicationContext中的事件处理是通过ApplicationEvent类和ApplicationListener接口来提供的。如果在上下文中部署一个实现了ApplicationListener接口的bean,那么每当一个ApplicationEvent发布到ApplicationContext时,这个bean就得到通知。
    Spring提供了三个标准事件,如下:
    ContextRefreshedEvent
    当ApplicationContext初始化或刷新时发送的事件。这里的初始化意味着:所有的bean被装载,singleton被预实例化,以及ApplicationContext已就绪可用
    ContextClosedEvent
    当使用ApplicationContext的close()方法结束上下文时发送的事件。这里的结束意味着:singleton bean 被销毁
    RequestHandledEvent
    一个与web相关的事件,告诉所有的bean一个HTTP请求已经被响应了(也就是在一个请求结束后会发送该事件)。注意,只有在Spring中使用了DispatcherServlet的web应用才能使用
     
    示例,Java类:

    java代码:
    1. public class T2 implements ApplicationListener{  
    2. @Override  
    3. public void onApplicationEvent(ApplicationEvent arg0) {  
    4. System.out.println("事件发生了=="+arg0);  
    5. }  
    6. }  
     
    配置文件
    <bean id="t2" class="cn.javass.Spring3.hello.T2"></bean>
    Resource 接口 :
    Spring的 Resource 接口是为了提供更强的访问底层资源能力的抽象,典型的是访问文件资源。基本的定义如下:

    java代码:
    1. public interface Resource extends InputStreamSource {  
    2.     boolean exists();  
    3.     boolean isOpen();  
    4.     URL getURL() throws IOException;  
    5.     File getFile() throws IOException;  
    6.     Resource createRelative(String relativePath) throws IOException;  
    7.     String getFilename();  
    8.     String getDescription();  
    9. }  
    10. public interface InputStreamSource {  
    11.     InputStream getInputStream() throws IOException;  
    12. }  
    可以使用ApplicationContext直接访问资源,示例如下:

    java代码:
    1. InputStream in = context.getResource("msg_en_GB.properties").getInputStream();  
    2. byte bs[] = new byte[100];  
    3. in.read(bs);  
    4. System.out.println("file content=="+new String(bs));  
    也可以向Bean里面注入资源,示例如下:
    在Java类当中添加:

    java代码:
    1. private Resource rs = null;  
    2. public void setRs(Resource rs){  
    3. this.rs = rs;  
    4. }  
    在配置文件中:

    java代码:
    1. <bean id="helloBean" class="cn.javass.Spring3.hello.HelloImpl">  
    2. <property name="rs" value="msg_en_GB.properties"></property>  
    3. </bean>  
    ApplicationContext能以声明的方式创建,在web.xml中配置如下:

    java代码:
    1. <context-param>  
    2.   <param-name>contextConfigLocation</param-name>  
    3.   <param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>  
    4. </context-param>  
    5. <listener>  
    6.   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
    7. </listener>  
     
    视频配套PPT,视频地址【 Spring3开发实战-独家视频课程 】
  • 相关阅读:
    Redis 6.0 新特性多线程连环13问!
    这些Java8官方挖过的坑,你踩过几个?
    读Hadoop3.2源码,深入了解java调用HDFS的常用操作和HDFS原理
    AI学习笔记:人工智能与机器学习概述
    千亿级互联网平台技术架构及背后那些事
    报告老板,微服务高可用神器已祭出,您花巨资营销的高流量来了没?
    千亿级平台技术架构:为了支撑高并发,我把身份证存到了JS里
    从技术思维角度聊一聊『程序员』摆地摊的正确姿势
    TryCatch包裹的代码异常后,竟然导致了产线事务回滚!
    SpringBoot集成邮件发送
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2592782.html
Copyright © 2011-2022 走看看