Spring IOC 详解

来源:互联网 发布:js 拆分字符串为数组 编辑:程序博客网 时间:2024/05/17 11:58

Bean元数据信息在Spring容器中对应物是由一个个BeanDefinition形成的Bean注册表,

Bean配置信息定义了Bean的实现以及依赖关系

[html] view plaincopyprint?
  1. <?xmlversion="1.0"encoding="UTF-8"?> 
  2. <beans   xmlns="http://www.springframework.org/schema/beans"  
  3.          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  4.          xsi:schemaLocation="http://www.springframework.org/schema/beans  
  5.          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 
  6.      <beanid="car"name="#car1"class="com.baobaotao.simple.Car"></bean>   
  7.      <beanid="boss"class="com.baobaotao.simple.Boss"></bean> 
  8. </beans> 

Bean的名字:

id作为Bean的名称,在IOC容器中必须唯一,由字母开头,后面可以是a-zA-Z0-9-.等

Bean也可以使用name进行命名,name没有字符上的限制,几乎可以使用任意字符,不过最好不要使用中文字符,name可以重复,getBean(beanName)时返回最后的那个bean

name可以写多个逗号用,隔开,例如name="p,q",id不可以

如果id和name都没有指定,name默认使用类的全称作为Bean名字,<bean id="boss" class="com.baobaotao.simple.Boss"></bean>要使用getBean("com.baobaotao.simple.Boss");来获得,如果出现下面的情况:

同一个容器中:

<bean class="com.baobaotao.simple.Boss"></bean>

<bean  class="com.baobaotao.simple.Boss"></bean>

<bean  class="com.baobaotao.simple.Boss"></bean>

第一个通过getBean("com.baobaotao.simple.Boss");或者getBean("com.baobaotao.simple.Boss#0");获得第二个通过getBean("com.baobaotao.simple.Boss#1");获得,第三个通过getBean("com.baobaotao.simple.Boss#2");获得


依赖注入:


属性注入:

[html] view plaincopyprint?
  1. <beanid="car"class="com.baobaotao.attr.Car"lazy-init="default"> 
  2.         <propertyname="brand"> 
  3.             <value> 
  4.                 <![CDATA[红旗&CA72]]> 
  5.             </value> 
  6.         </property> 
  7.         <propertyname="maxSpeed"> 
  8.             <value>200</value> 
  9.         </property> 
  10.         <propertyname="price"value="2000.00"/> 
  11.     </bean> 
属性名以小写开头,应符合java命名规范,如果出现特殊情况,也要保持属性前两个字母,要么全部大写要么全部小写

[html] view plaincopyprint?
  1. <propertyname="favorites"> 
  2.     <set> 
  3.         <value>看报</value> 
  4.         <value>赛车</value> 
  5.         <value>高尔夫</value> 
  6.     </set> 
  7. </property> 

[html] view plaincopyprint?
  1. <list> 
  2.                 <value>看报</value> 
  3.                 <value>赛车</value> 
  4.                 <value>高尔夫</value> 
  5.         </list> 

[html] view plaincopyprint?
  1. <list> 
  2.    <ref bean="usera"/> 
  3.    <refbean="userb"/> 
  4.    <ref bean="userc"/> 
  5. </list> 


[html] view plaincopyprint?
  1. <array> 
  2.       <value>看报</value> 
  3.        <value>赛车</value> 
  4. </array> 


[html] view plaincopyprint?
  1. <map> 
  2.                 <entry> 
  3.                     <key> 
  4.                         <value>AM</value> 
  5.                     </key> 
  6.                     <value>会见客户</value> 
  7.                 </entry> 
  8.                 <entry> 
  9.                     <key> 
  10.                         <value>PM</value> 
  11.                     </key> 
  12.                     <value>公司内部会议</value> 
  13.                 </entry> 
  14.             </map> 

[html] view plaincopyprint?
  1. <props> 
  2.                 <propkey="jobMail">john-office@baobaotao.com</prop> 
  3.                 <propkey="lifeMail">john-life@baobaotao.com</prop> 
  4.             </props> 

[html] view plaincopyprint?
  1. <propertyname="car"> 
  2.             <reflocal="car"/> 指向另一个bean的id 
  3.         </property> 
<ref>元素可通过三个属性引用其他容器中的Bean:

bean:同一个容器或者父容器中的Bean(常见)

local:同一配置文件中定义的bean

parent:父容器中的bean

父子容器允许有相同id的bean,子容器可以访问父容器中的bean,父容器却不能访问子容器中的bean,我们自己很少写父子容器,下面是父子容器的简单代码:

[java] view plaincopyprint?
  1. ClassPathXmlApplicationContext parentFactory=new ClassPathXmlApplicationContext(new String[]{"parent.xml"}); 
  2. ClassPathXmlApplicationContext childFactory=new ClassPathXmlApplicationContext(new String[]{"parent.xml"},parentFactory); 
  3. childFactory.getBean("bean"); 

[html] view plaincopyprint?
  1. <beanid="sportsCar"class="com.baobaotao.attr.SportsCar"> 
  2.     <propertyname="brand"> 
  3.         <beanid="car.brand" 
  4.             class="org.springframework.beans.factory.config.PropertyPathFactoryBean"> 
  5.         </bean> 
  6.     </property> 
  7. </bean> 

上面的是内部bean,虽然提供了id,但是其他bean都不能使用它,name,id,scope属性都会被忽略,这里scope默认为prototype,如果sportsCar是prototype类型的话,每次得到一个"新"的sportsCar时,都会调用一次brand对应的bean的构造方法,如果是在外面定义的bean,scope默认值就是singleton的,上面定义的sportsCar就是singleton类型的

[html] view plaincopyprint?
  1. <propertyname="IDCode"><value></value></property> 值是空字符串 
[html] view plaincopyprint?
  1. <propertyname="IDCode"><null/></property> 值是null 
[html] view plaincopyprint?
  1. <propertyname="car.name"><null/></property>   级联属性 会调用 .getCar().setName(null); 如果car不存在为null,就会出错 
如果子bean继承了父bean的同名集合元素,可以把父子合并:

[html] view plaincopyprint?
  1. <beanid="parentBoss"abstract="true" 
  2.         class="com.baobaotao.attr.Boss"> 
  3.         <propertyname="favorites"> 
  4.             <set> 
  5.                 <value>看报</value> 
  6.                 <value>赛车</value> 
  7.                 <value>高尔夫</value> 
  8.             </set> 
  9.         </property> 
  10.     </bean> 
  11.     <beanid="childBoss"parent="parentBoss"> 
  12.         <propertyname="favorites"> 
  13.             <setmerge="true"> 
  14.                 <value>爬山</value> 
  15.                 <value>游泳</value> 
  16.             </set> 
  17.         </property> 
  18.     </bean>    

为了简化操作可以使用p命名空间,p:属性名="xxx"   p:属性名_ref="xxx",例如:

[html] view plaincopyprint?
  1. <beanid="car"class="com.baobaotao.ditype.Car" 
  2.    p:brand="红旗&CA72" 
  3.    p:maxSpeed="200" 
  4.    p:price="20000.00"/> 
  5. <beanid="boss"class="com.baobaotao.ditype.Boss" 
  6. p:car-ref="car"/> 

如果希望配置集合类型的bean,那么可以使用util命名空间

[html] view plaincopyprint?
  1. <util:listid="favoriteList1"list-class="java.util.LinkedList"> 
  2.         <value>看报</value> 
  3.         <value>赛车</value> 
  4.         <value>高尔夫</value> 
  5.     </util:list> 
[html] view plaincopyprint?
  1. <util:setid="favoriteSet1" > 
  2.         <value>看报</value> 
  3.         <value>赛车</value> 
  4.         <value>高尔夫</value> 
  5. </util:set> 
[html] view plaincopyprint?
  1. <util:mapid="emails1"> 
  2.         <entrykey="AM"value="会见客户"/> 
  3.         <entrykey="PM"value="公司内部会议"/> 
  4.     </util:map> 
[html] view plaincopyprint?
  1. <util:propertiesid="emailProps1" 
  2.         location="classpath:com/baobaotao/fb/mails.properties"/> 

当然该util类还有其他用处,例如:

[html] view plaincopyprint?
  1. <propertyname="brand"> 
  2.             <util:constant 
  3.                 static-field="com.baobaotao.fb.CarBrandType.HONG_QI"/> 
  4.         </property> 
[html] view plaincopyprint?
  1. <propertyname="brand"> 
  2.             <util:property-pathpath="boss.car.brand"/> 找到id为boss的bean取出car中的brand属性 
  3.         </property> 




构造方法注入:

按类型:

[html] view plaincopyprint?
  1. <beanname="constructorBean"class="bean.ConstructorBean">  
  2. <constructor-argtype="java.lang.String"value="1"/>  
  3. <constructor-argtype="java.lang.Integer"value="2"/>  
  4. </bean>  
如果构造方法中有两个相同的类型就出问题了,按索引值:

[html] view plaincopyprint?
  1. <beanname="constructorBean"class="bean.ConstructorBean">  
  2. <constructor-argindex="0"value="1"/>  
  3. <constructor-argindex="1"value="2"/>  
  4. </bean>  

两者联合使用也可以:

[html] view plaincopyprint?
  1. <beanname="constructorBean"class="bean.ConstructorBean">  
  2. <constructor-argindex="0"type="java.lang.String"value="1"/>  
  3. <constructor-argindex="1"type="int"value="2"/>  
  4. </bean>  

使用构造方法注入时有可能出现循环依赖,比如 Bean1中有Bean2属性,Bean2中也有Bean1属性,两者都使用构造注入就出现了循环依赖问题


工厂方法注入:(参考:http://550516671-qq-com.iteye.com/blog/803999)

非静态方法:

[java] view plaincopyprint?
  1. public class CarFactory {    
  2.    
  3.     public Car createHongQiCar(){    
  4.         Car car = new Car();    
  5.         car.setName("红旗");    
  6.         car.setSpeed("200");    
  7.         return car;    
  8.     }    
  9.         
  10. }   
[html] view plaincopyprint?
  1. <beanid="carFactory"class="com.lbx.factory.CarFactory"></bean>   
  2. <bean id="car"factory-bean="carFactory"factory-method="createHongQiCar"></bean> 

静态方法:

[java] view plaincopyprint?
  1. public class CarFactory2 {    
  2.    
  3.     public static Car createHongQiCar(){    
  4.         Car car = new Car();    
  5.         car.setName("红旗");    
  6.         car.setSpeed("200");    
  7.         return car;    
  8.     }    
  9.         
  10. }   

[html] view plaincopyprint?
  1. <beanid="car"class="com.lbx.factory.CarFactory2"factory-method="createHongQiCar"></bean>   


Bean的作用域通过scope属性来指定,值如下:(参考:http://blog.csdn.net/mastermind/article/details/1932787)

singleton

当把一个bean定义设置为singleton作用域时,Spring IOC容器只会创建该bean定义的唯一实例。这个单一实例会被存储到单例缓存(singleton cache)中,并且所有针对该bean的后续请求和引用都将返回被缓存的对象实例,这里要注意的是singleton作用域和GOF设计模式中的单例是完全不同的,单例设计模式表示一个ClassLoader中只有一个class存在,而这里的singleton则表示一个容器对应一个bean,也就是说当一个bean被标识为singleton时候,spring的IOC容器中只会存在一个该bean。

prototype
prototype作用域部署的bean,每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)都会产生一个新的bean实例,相当与一个new的操作,对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责,容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法,而对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被singleton作用域bean占用资源的一种可行方式是,通过使用bean的后置处理器,该处理器持有要被清除的bean的引用。)

request
request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效

session
session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效

global session
global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。如果你在web中使用global session作用域来标识bean,那么web会自动当成session类型来使用

自定义bean装配作用域
在spring2.0中作用域是可以任意扩展的,你可以自定义作用域,甚至你也可以重新定义已有的作用域(但是你不能覆盖singleton和prototype),spring的作用域由接口org.springframework.beans.factory.config.Scope来定义,自定义自己的作用域只要实现该接口即可

bean之间的依赖关系可以自动装配,在beans中可以配置属性default-autowire(全局对所有bean生效),在bean中设置autowire来实现。值如下:

no:通过ref元素指定依赖

byName:在容器中寻找和需要自动装配的属性名相同的Bean(或ID),如果没有找到相符的Bean,该属性就没有被装配上.

byType:在容器中寻找一个与需要自动装配的属性类型相同的Bean;如果没有找到相符的Bean,该属性就没有被装配上.

Constructor:在容器中查找与需要自动装配的Bean的构造方法参数一致的一个或者多个Bean,如果存在不确定的Bean或构造方法,容器会抛出异常org.springframework.beans.factory.UnsatisfiedDependencyException.

Autodetect:使用constructor或byType来自动装配。如果提供了默认的无参构造,就使用byType

方法注入:

让bean实现BeanFactoryAware,在方法中写:return (Car)factory.getBean("car");用在单例的bean中每次获取某属性时都要求是prototype类型的对象

lookup注入:bean中的某方法返回值并不是执行该方法得到的结果,而是容器中的某个bean,这里是通过为bean动态创建子类或实现类的方式实现的,例如

[html] view plaincopyprint?
  1. <beanname="userManager" class="research.spring.beanfactory.ch2.UserManager"> 
  2.        <lookup-methodname="getUserDao"bean="userDao"/> 
  3. </bean> 

这里如果调用userManager中的getUserDao会返回userDao,无论UserManager中getUserDao有无实现

方法替换:

一个bean中的方法用另一个bean中相同签名的方法替换掉,例如:

[html] view plaincopyprint?
  1. <beanid="boss2"class="com.baobaotao.injectfun.Boss2"/> 
  2. <bean id="boss1"class="com.baobaotao.injectfun.Boss1"> 
  3.    <replaced-methodname="getCar"replacer="boss2"></replaced-method> 
  4. t;/bean> 


bean之间的关系:

继承:

父bean一般设置abstract=“true”表示只是为了代码被复用,子bean写属性parent=“parentbeanid”,这样子bean就可复制了父bean的配置信息

依赖:

在bean中写depends-on="beana" 那么保证在实例化当前bean时一定有了被依赖的bean了;即使不写依赖关系spring也会自动处理,这个属性主要在代码中依赖很不明显的情况下使用

引用:

把之前的ref标签改为idref标签,这样如果写错了id值,不是在运行时用到再报错,而是在容器启动时就报错了

如果需要一个配置文件包含其他配置文件,可以使用<import resource="classpath:com/auscend/beans.xml"/>

在bean标签中属性init-method="init" 定义实例化bean之后调用的方法, destroy-method="destory"定义要销毁bean之前要调用的方法,这两个方法都是定义在bean内部的方法,要注意如果是prototype的bean,容器只负责创建,不负责销毁。

以上都是在xml中使用,如果要用注解的话,参考另一篇:spring IOC annotation

原创粉丝点击