Spring核心Bean工厂装配__Spring学习笔记

在Spring 中最核心的组件是bean工厂,它提供了基本的反向控制和依赖注入的能力。Spring 是一种无侵入性的框架,被bean工厂管理的组件无须知道spring的存在。bean工厂负责打造bean,并注射它们之间的依赖。这些bean会彼此协作。 spring中最基本的BeanFactory接口org.springframework.beans.factory.BeanFactory ,它提供一些工厂的基本方法。

package  org.springframework.beans.factory;
import  org.springframework.beans.BeansFaException;

public   interface  BeanFacotory
{
      
//根据名字查找bean
      Object getBean(String name) throws BeansException;
      
//检测目标bean是否具有指定的类型
      Object getBean(String name,Class requiredType);
      
      
boolean containsBean(String name);
      
//检测某个bean 是否 singleton 如果是 ,则返回一个共享的实例。否则每次创建一个新的对象实例
      boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
      Class getType(String name) 
throws NoSuchBeanDefinitionException;
      String getAliases(String name) 
throws NoSuchBeanDefinitionException;

}

实例化bean工厂的三种方法:

package  com.cao.spring;
import  org.springframework.beans.factory.BeanFactory;
import  org.springframework.core.io.Resource;
import  org.springframework.beans.factory.xml.XmlBeanFactory;
public   class  BeanFactoryUsage  {
    
public static void main(String[] args){
        
//从文件系统资源实例化BeanFactory;
        
//默认从当前的工程目录下开始找。(工程目录/com/cao/文件目录)
        org.springframework.core.io.Resource res = new org.springframework.core.io.FileSystemResource("com/cao/bean.xml");
        org.springframework.beans.factory.BeanFactory factory 
= new org.springframework.beans.factory.xml.XmlBeanFactory(res);
        
        System.out.println(
"OK"+factory);
        
        
        
//从classpath下的资源实例化BeanFactory (Eclipse 中的 src/data目录下)
        org.springframework.core.io.Resource  resClasspath = new org.springframework.core.io.ClassPathResource("data/bean2.xml");
        org.springframework.beans.factory.BeanFactory factory2 
= new XmlBeanFactory(resClasspath);
        
        
//使用ApplicationContext  从 classpath下的xml 文件实例化BeanFacotory
        String classPath = "data/";
        org.springframework.context.ApplicationContext appContext 
= new org.springframework.context.support.ClassPathXmlApplicationContext(new String[]{classPath+"bean2.xml"});
        
//直接生成类工厂的实例。
        org.springframework.beans.factory.BeanFactory factory3 = (BeanFactory)appContext;
    
    }


}

Bean工厂的处理过程
(1) Spring中,一个bean 定义被抽象为BeanDefinition接口每次实例化XMLBeanFactory 或ClassPathXmlApplicationContext时都会对xml 进行解析,形成AbstractBeanDefinition向BeanFactory进行注册。
(2) 在这个过程中有些BeanFactory还会进行预初始化 (如ClassPathXmlApplicatitonContxt). 这样客户代码在调用getBean的时候就可以迅速取得Bean的实例了。


使用静态工厂方法创建Bean

通过静态工厂方法创建Bean,使用factory-method属性来指定工厂方法名。 目标bean的class属性正是哪个包含静态工厂的类。
<bean id="legacyBean" class="com.cao.LegacySingleton" factory-method="getInstance"/>
这个bean 配置并未指定返回对象的类型,只是指定包含工厂方法的类。并且该类的 getInstance 方法必须是静态方法。


使用实例工厂方法创建Bean

通过实例工厂方法创建Bean ,类似于使用静态工厂,它调用一个已知的bean的工厂方法来创建新的bean
<bean id="legacyBean" class="com.cao.ConcreteLegacyFactory" /> <bean id="product" factory-bean="legacyBean" factory-method="make"/>
classs 指定了具体的Bean工厂类型 。 factory-bean 映射 id ="legacyBean" ;  factory-method 指定了生产bean 的方法。

Spring中,Ioc/DI 主要有两种形式。
(1)基于设值方法的依赖注入,通过bean的setter方法实现。在构造方法或其他方法创建bean的实例后,进setter注入也是完全允许的。
(2)基于构造方法的依赖注入,通过调用带有多个参数的构造来实现的。这些参数代表bean的协作者(要注入的哪个实例)或属性。spring将通过特定参数的 静态工厂方法来构造bean 也归到此类。


1,基于setter的依赖的bean 的配置

< bean  id ="setterBean"  class ="com.cao.SetterBean"   >
   
< property  name ="beanOne" >  
       
< ref  bean ="anotherBean" />   
   
</ property >  
   
< property  name ="beanTwo" >  
        
< ref  bean ="yetAntherBean" />
    
</ property >
    
< property  name ="integerProperty" >
           
< value > 1 < value >
    
</ property >
</ bean >
< bean  id ="anotherBean"  class ="com.cao.AnotherBean"   />
< bean  id ="yetAntherBean"  class ="com.cao.YetAnotherBean"   />

 

// 实现的类。
package  com.cao.spring;

public   class  SetterBean  {
    
private AnotherBean beanOne;
    
private YetAnotherBean beanTwo;
    
private int i;
    
    
    
    
public void setBeanOne(AnotherBean beanOne) {
        
this.beanOne = beanOne;
    }


    
public void setIntegerProperty(int i) {
        
this.i = i;
    }


    

    
public void setBeanTwo(YetAnotherBean beanTwo) {
        
this.beanTwo = beanTwo;
    }


    @Override
    
public String toString() {
        
// TODO Auto-generated method stub
        return "beanOne"+this.beanOne+":beanTwo" +this.beanTwo+":int"+this.i;
    }

    
    
    
public static void main(String[] args){
        org.springframework.core.io.Resource rs 
= new org.springframework.core.io.ClassPathResource("data/setter.xml");
        org.springframework.beans.factory.BeanFactory factory
= 
             
new org.springframework.beans.factory.xml.XmlBeanFactory(rs);
        
        System.out.println(factory.getBean(
"setterBean"));
    }

    
}

比较类和配置文件 ,可以看出。<property name="beanOne">  的name对应类中的一个setXXX方法 xxx和name的值满足javaBean的定义。
                             <ref bean="anotherBean"/>  bean 的值引用另一个bean的id   <bean id="anotherBean" class="com.cao.AnotherBean" />

事实上就是将com.cao.AnotherBean这个类通过 setXXX方法设置到类com.cao.SetterBean中作为它的成员属性。

 

2,基于构造方法的DI配置

< beans >
    
< bean  id ="constructBean"  class ="com.cao.spring.ConstructBean" >
        
< constructor-arg >
            
< ref  bean ="beanOne" />
        
</ constructor-arg >
        
< constructor-arg >
            
< ref  bean ="beanTwo" />
        
</ constructor-arg >
        
        
< constructor-arg  index ="2" >
            
< value  type ="int" > 1 </ value >
        
</ constructor-arg >
    
</ bean >
    
    
< bean  id ="beanOne"  class ="com.cao.spring.AnotherBean" />
    
< bean  id ="beanTwo"  class ="com.cao.spring.YetAnotherBean" />
</ beans >

 

// 实现类
package  com.cao.spring;

import  org.springframework.beans.factory.BeanFactory;
import  org.springframework.beans.factory.xml.XmlBeanFactory;
import  org.springframework.core.io.ClassPathResource;
import  org.springframework.core.io.Resource;

public   class  ConstructBean  {
    
private AnotherBean beanOne;
    
private YetAnotherBean beanTwo;
    
private int i;
    
public ConstructBean(AnotherBean beanOne, YetAnotherBean beanTwo, int i) {
        
super();
        
this.beanOne = beanOne;
        
this.beanTwo = beanTwo;
        
this.i = i;
    }

    
public String toString() {
        
// TODO Auto-generated method stub
        return "beanOne"+this.beanOne+":beanTwo" +this.beanTwo+":int"+this.i;
    }

    
    
public static void main(String[] args){
        Resource rs 
= new ClassPathResource("data/construct.xml");
        BeanFactory factory 
= new XmlBeanFactory(rs);
        System.out.println(factory.getBean(
"constructBean"));
        
    }

}

做了一个测试发现 <constructor-arg> 配置中的参数的顺序不一定与 构造方法的参数的顺序相同。通过 index="2"来指定参数的位置(从0开始)

3,基于构造方法的DI配置__调用工厂方法(静态或非静态)

< beans >
    
< bean  id ="diFactory"  class ="com.cao.spring.DIFactory" />
          
<!-- 在实例工厂方法中通过factory-bean来指定包含该方法的工厂类。factory-method指定创建的方法 -->
    
< bean  id ="product"  factory-bean ="diFactory"  factory-method ="make" >
        
< constructor-arg >
            
< ref  bean ="beanOne" />
        
</ constructor-arg >
        
< constructor-arg >
            
< ref  bean ="beanTwo" />
        
</ constructor-arg >
        
< constructor-arg >
            
< value > 1 </ value >
        
</ constructor-arg >
    
</ bean >     
    
    
< bean  id ="beanOne"  class ="com.cao.spring.AnotherBean" />
    
< bean  id ="beanTwo"  class ="com.cao.spring.YetAnotherBean" />
    
<!-- 静态工厂方法中factory-method指定创建的方法(静态) 包含改工厂的类为这个工厂本身 -->
    
< bean  id ="productStatic"  class ="com.cao.spring.DIFactory"  factory-method ="create" >
        
< constructor-arg >
            
< ref  bean ="beanOne" />
        
</ constructor-arg >
        
< constructor-arg >
            
< ref  bean ="beanTwo" />
        
</ constructor-arg >
        
< constructor-arg >
            
< value > 1 </ value >
        
</ constructor-arg >
    
</ bean >
</ beans >

Spring自动装配(autowiring)
1, no     不进行自动装配,这是spring的默认配置。
2,byName 通过属性名进行自动装配。Spring 查找待装配的bean属性同名的bean。
(测试:发现当找不到匹配的时候取其默认值null / 0 引用类型为null ,基本数据类型为0 )
3,byType Spring 查找待装配的bean 同类型的bean,不过如果找到多于一个一上的bean 是会抛异常的。
(测试:哪怕是同一个bean对应不同的id也会报错,如果不足是不会报错只是为空。如果设置了 dependency-check="objects" 不足也会报错)
4,constructor 类似于 byType 。匹配构造子参数,而非属性。
5,autodetect  让spring 自动选择construtor 或 byType如果找到的是一个默认构造方法则使用byType

依赖检查:
模式:   说明
none     不进行依赖检查
simple   对基本类型和集合进行依赖检查
objects   对协作者(你所注入的哪个类)进行依赖检查
all      对协作者,基本类型和集合都进行检查

 

使用collection(集合) 元素定义集合。

< beans >
    
< bean  id ="complext"  class ="com.cao.spring.ComplexBean" >
                 
<!-- java.util.Properties -->
        
< property  name ="people" >
            
< props >
                
< prop  key ="name" > Spirti.J </ prop >
                
< prop  key ="age" > 25 </ prop >
            
</ props >
        
</ property >
        
        
< property  name ="comeList" >
            
< list >
                
<!--  配置文件中的<value>之间也不可以带空格spring不处理空格  -->
                
< value > list中的第一个元素,下一个元素是其他bean的引用 </ value >
                
< ref  bean ="anotherBean" />
            
</ list >
        
</ property >
        
        
< property  name ="someMap" >
            
< map >
                
<!--  测试:当出现同名的key 时 后面的会覆盖前面的  -->
                
< entry  key ="key-String"  value ="我是字符串甲" />
                
< entry >
                    
< key >< value > key-String </ value ></ key >
                    
< value > 我是字符串乙 </ value >
                
</ entry >
                
                
<!--  两种写法是等效的  -->
                
< entry  key ="key-ref"  value-ref ="anotherBean" ></ entry >
                
                
< entry >
                    
< key >< value > key-ref </ value ></ key >
                    
<!--  ref在这里提供一个协作者相当于一个<value>作为一个单独的元素  -->
                    
< ref  bean ="anotherBean" />     
                
</ entry >
            
</ map >
        
</ property >
        
        
< property  name ="someSet" >
            
< set >
                
<!--  测试:当set转化为数组时,与配置的顺序有关系  -->
                
< value > 我是字符串丙 </ value >
                
< ref  bean ="anotherBean" />
            
</ set >
            
        
</ property >
        
    
</ bean >
    
< bean  id ="anotherBean"  class ="com.cao.spring.AnotherBean" />
</ beans >

通过嵌套bean来定义内部bean
内部bean不需要任何id 或则 singleton 标记,它是一种匿名的内部原形,它的实例仅供外部类使用。

< bean  id ="outerBean"  class ="com.cao.spring.OuterBean" >
        
< property  name ="target" >
            
<!--  内部bean是不需要id的.测试:加了也不报错.但无意义  -->
            
< bean   class ="com.cao.spring.ComplexBean" >
                
< property  name ="people" >
                    
< props >
                        
< prop  key ="name" > Spirit.J </ prop >
                        
< prop  key ="age" > 25 </ prop >
                    
</ props >
                
</ property >
                         
</ bean >
        
</ property >

</ bean >


方法注入
为什么需要方法注入?
假设一个singleton beanA 和一个non-singleton beanB 那么容器仅仅会对beanA实例化一次也就就只有一次机会去设置它的属性。所以无法每次为beanA
提供一个新的beanB的实例。因为beanB 是BeanA的属性。

使用lookup 方法注入,lookup 方法能够在运行时重写bean的抽象或具体方法,返回或创建容器中其他bean的实例。被创建的通常是一个non-singleton bean
但也可以是 singleton 的。(spring 通过CGLIB来实现这种注入)

< beans >
    
< bean  id ="anotherBean"  class ="com.cao.spring.AnotherBean"  singleton ="false" />
    
< bean  id ="myBean"  class ="com.cao.spring.MyLookUpBean" >
        
< lookup-method  name ="newAnotherBean"  bean ="anotherBean" />
    
</ bean >
</ beans >

MyLookUpBean中 有方法
protected AnotherBean newAnotherBean(){
  return null;
 }
在MyLookUpBean调用这个方法时候。spring 都会重写这个方法并返回一个 AnotherBean 的实例。

替换任意方法。
通过replace-method元素替换以存在的方法实现。
使用方法替换需要实现的接口 org.springfarmework.beans.factory.support.MethodReplacer接口。

     < bean  id ="myReplaceBean"  class ="com.cao.spring.MyValueCalculator" >
        
< replaced-method  name ="computeValue"  replacer ="aaa" >
            
< arg-type > String </ arg-type >
            
        
</ replaced-method >
    
</ bean >
    
< bean  id ="aaa"  class ="com.cao.spring.ReplacementComputeValue" />

原本MyValueCalculator类中有一个方法。
public String computeValue(String input){
  System.out.println("原始的计算");
  return "";
 }

结果在运行时被 ReplacementComputeValue 类中的实现所替换。改类实现了MethodReplacer接口。覆盖该接口中的 reimplement方法的实现作为
computeValue 实现的替换。<replaced-method>中的name 指定了要替换的方法。 replacer属性指定实现MethodReplacer接口的类。

Bean的原生属性
Spring 提供了一些生命周期的标记接口,包括InitializingBean 和DisposableBean. 他们可以改变bean的行为. 使得bean在初始化和析构时执行特定的处理
InitializingBean 中有 afterpropertiesSet() 方法 一个bean 实现了InitializingBean接口后并实现 这个方法可以用来完成初始化工作。但这样做有一个
弊端,就是 bean 与 spring 框架 发生了耦合。 更好的方法是通过在配置文件中用 init-method 属性来指定一个方法来完成初始化工作。
DisposableBean 和 destroy-method 属性的用法 类似 初始化。

Bean先完成属性的注入 然后初始化 当工厂销毁的时候。再销毁
在 singleton bean的情况下 即使多次 factory.getBean("someBean"); 这些工作都只会执行一次 包括属性的注入。

< bean  id ="someBean"  class ="com.cao.spring.SomeBean"  init-method ="afterPropertiesSetOfMy"  destroy-method ="destroyOfMy" >

获取Bean自身的相关信息
BeanNameAware 接口 Spring 容器通过该接口调用bean 获取相关信息。获取的时机是 注入属性之后,初始化(init-method) 方法之前。

public   class  AwareBean  implements  org.springframework.beans.factory.BeanNameAware,org.springframework.beans.factory.BeanFactoryAware  {

    
private String beanName;
    
private BeanFactory factory;
    
    
public void setBeanName(String arg0) {
         
this.beanName = arg0;    
    }

    
    
public void setBeanFactory(BeanFactory arg0) throws BeansException {
        
this.factory = arg0;
    }

    
    
public void doInit(){
        String name
= factory.getClass().getName();
        
        System.out.println(
"我是"+name+"创建的"+beanName);
    }

    
public static void main(String[] args){
        org.springframework.core.io.Resource rs  
= 
             
new ClassPathResource("data/beanAware.xml");
        
        org.springframework.beans.factory.BeanFactory factory 
= 
            
new XmlBeanFactory(rs);
        
        factory.getBean(
"beanAware");
    }

}

分析代码我们可以发现 一个Bean 竟然可以 获得它的创建者(BeanFactory)的引用 。那么我们就可以利用这个引用获得更多的 信息。还可以将这个
BeanFactory 强制转型为合适的工厂类型 从而获得更多的相关信息。

父子bean的定义
一个bean的定义可以包含大量的配置信息,而一个子bean的定义可以继承父bean的配置。并允许覆盖和添加一些其他信息。在Spring中可以把父bean看成是字bean
的模板。使用父子bean 可以减少很多重复的工作。子bean使用parent属性来指向父bean。

         < bean  id ="father"  class ="com.cao.spring.ParentBean" >
        
< property  name ="name"  value ="parent" />
        
< property  name ="age"  value ="20" />
    
</ bean >
                                                       
<!--  parent属性指向父bean  -->
    
< bean  id ="child"  class ="com.cao.spring.ChildBean"  parent ="father" >
        
<!--  覆盖父baen中的name 属性的值  -->
        
< property  name ="name"  value ="override" />
        
</ bean >

1> ParentBean father = (ChildBean) factory.getBean("child");    
2> ParentBean father = (ParentBean) factory.getBean("child");   
father.getName();得到的都是 override说明 factory工厂创建的真正的实例还是ChildBean 当id = "Child" 的bean 没有class属性时可以用2>方法转型
得到的真正实例仍然是ChildBean

容器忽略既没有class也没有parent属性的bean

使用后理器(Post-Processor)
一个Bean post-processor 需要实现BeanPostProcessor接口。它有两个回调方法 postProcessBeforeInitialzation() 和 postProcessAfterInitialization();
如果一个 bean工厂注册了post-processor,那么对与所创建的每个bean实例。初始化方法前后都会得到一个回调。
测试发现: 使用BeanFactory 装配时候不需要 在配置文件中映射你的 后处理类。它会被自动调用。但要手动注册 
        org.springframework.beans.factory.config.ConfigurableBeanFactory config= new XmlBeanFactor(rs);  
         org.springframework.beans.factory.config.BeanPostProcessor postProcessor = new MyBeanPostProcessor();
   config.addBeanPostProcessor(postProcessor);

          使用ApplicationContext 工厂自动装配时。要在配置文件映射该类。<bean id="postBean" class="com.cao.spring.MyBeanPostProcessor"/>
          只要你的配置文件有 MyBeanPostProcessor 这个类。好象id是可以随便取的。


使用BeanFactoryPostProcessor
实现 BeanFacotoryPostProcessor的类就是一个Bean factory post-processor,它可以在bean 工厂创建后对整个bean 做某种修改。

PropertyPlaceholderConfigurer
它用来将Spring配置文件中的属性只抽离到一个单独的java Properties文件。
这样可以避免在Spring的配置进行修改。

完整的参考测试工程源码 Eclipse3.2+Tomcate5.0       http://download.csdn.net/source/470868

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值