首页 > 代码库 > 深入了解Spring中的容器
深入了解Spring中的容器
1.创建Bean的3种方式
1.1使用构造器创建bean实例
这是最常见的方式,如果不采用构造注入,bean类需要有默认构造函数。如果采用构造注入,则需要配置xml文件的<constructor-arg>
1.2使用静态工厂方法创建bean
最典型的工厂方法如
1 package spi; 2 3 public class PersonFactory { 4 public static Person getPerson(String arg) { 5 if (arg.equalsIgnoreCase("Chinese")) { 6 return new Chinese(); 7 } else { 8 return new American(); 9 } 10 } 11 }
如果在Spring容器中配置一个bean,bean的实例希望由上的静态工厂方法反回,则可以在bean中使用 factory-method来指定工厂方法,并用<constructor-arg>指定参数。
1 <bean id="chinese" class="spi.PersonFactory" factory-method="getPerson"> 2 <constructor-arg value="chinese" /> 3 </bean>
1.3实例化工厂方法创建bean
这与上面的使用静态工厂方法创建bean高度相似,区别是这里是需要先创建工厂的实例。工厂方法如下
1 package spi; 2 3 public class PersonFactory { 4 public Person getPerson(String arg) { 5 if (arg.equalsIgnoreCase("Chinese")) { 6 return new Chinese(); 7 } else { 8 return new American(); 9 } 10 } 11 }
在xml配置中,除了使用factory-method来指定bean的实例化工厂方法外,还需要使用factory-bean指定工厂实例的bean
1 <bean id="chinese" class="spi.PersonFactory" factory-method="getPerson" factory-bean="personFactory"> 2 <constructor-arg value="chinese" /> 3 </bean> 4 <bean id="personFactory" class="spi.PersonFactory" />
2.bean的继承
可以在Spring中使用abstract属性将一个bean定义一个模板配置,这叫做一个抽象bean。抽象bean不能被实例化,只是为了降低配置文件的冗余而存在的,只能由子bean继承,子类则使用parent属性指定父类bean。下面是一个例子,
1 <bean id="personTemplate" abstract="true"> 2 <property name="name" value="zhangsan" /> 3 <property name="axe" value="stoneAxe" /> 4 </bean> 5 6 <bean id="chinesePerson" parent="personTemplate"> 7 <property name="axe" value="steelAxe" /> 8 </bean>
这个例子中,子类bean chinesePerson将会从父类继承name属性,但是会覆盖父类的axe属性。
注意的是并非父类所有属性子类都能继承,depends-on, autowire, singleton, scope, lazy-ini 这些属性只能从子类本身获取或采用默认值。
并且Spring容器bean的继承可以在不同类型的bean之间存在。
3.工厂bean: FactoryBean接口
Spring容器会检查所有bean,如果发现某个bean实现了FactoryBean接口,就会调用接口的getObject(),其返回值才会作为真正的bean。
开发人员可以重写getObject()方法供Spring调用,例如下面,
1 package spi; 2 3 import java.lang.reflect.Field; 4 5 import org.springframework.beans.factory.FactoryBean; 6 7 public class GetField implements FactoryBean<Object>{ 8 private String targetClass; 9 private String targetField; 10 11 public String getTargetClass() { 12 return targetClass; 13 } 14 15 public void setTargetClass(String targetClass) { 16 this.targetClass = targetClass; 17 } 18 19 public String getTargetField() { 20 return targetField; 21 } 22 23 public void setTargetField(String targetField) { 24 this.targetField = targetField; 25 } 26 27 @Override 28 public Object getObject() throws Exception { 29 // TODO Auto-generated method stub 30 Class<?> clazz = Class.forName(targetClass); 31 Field field = clazz.getField(targetField); 32 return field.get(null); 33 } 34 35 @Override 36 public Class<?> getObjectType() { 37 // TODO Auto-generated method stub 38 return Object.class; 39 } 40 41 @Override 42 public boolean isSingleton() { 43 // TODO Auto-generated method stub 44 return false; 45 } 46 47 }
这个类实现了FactoryBean接口,可以返回任何我们需要的对象,功能非常强大,在Spring中可以做如下配置,
1 <bean id="getField" class="spi.GetField"> 2 <property name="targetClass" value="spi.Chinese" /> 3 <property name="targetField" value="axe" /> 4 </bean>
测试代码如下,
1 public static void test6() { 2 ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); 3 System.out.println(ctx.getBean("getField")); 4 }
程序输出:North
Spring提供的工厂Bean,大多以FactoryBean结尾,用于生产指定类型的bean,工厂bean是Spring的重要工具之一。
4.获取Bean本身id
就像Bean希望获取所在的ApplicationContext实例一样,有时候Bean还希望获取XML中配置的id名称,这种情况需要bean实现BeanNameAware接口,过程与实现ApplicationContextAware类似。并且实现BeanNameAware接口的setBeanName()方法供Spring调用,例如下面,
1 public class Chinese implements Person, BeanNameAware { 2 private String beanName; 3 @Override 4 public void setBeanName(String arg0) { 5 this.beanName = beanName; 6 7 } 8 ...
这样容器创建bean时候就会调用这个setBeanName()方法,bean就能获取自己的id名称了。例如还可以在Chinese类中添加下面的方法,
1 public void info(){ 2 System.out.println("我在XML中的id名称为:"+beanName); 3 }
5.强制初始化bean
某些情况下,容器中的依赖并不是非常直接,初始化bean时候,如果依赖的bean尚未初始化,有可能会出错,这种情况下我们希望先初始化依赖的bean。
我们可以使用depebds-on属性来强制容器先初始化依赖额bean,例如
1 <bean id="chinese" class="spi.Chinese" depends-on="steelAxe" /> 2 <bean id="steelAxe" class="spi.SteelAxe" />
6.容器中Bean的生命周期
对于singleton的bean,Spring可以精确控制生命周期,因此可以在bean的各个阶段指定各种行为。
例如在bean依赖注入之后,或者销毁之前,可以插入指定动作。
6.1依赖关系注入之后的行为
有两种方式可以在Bean属性设置成功之后执行特定行为。
- 一是使用init-method属性
这种方式要求在bean中定义一个回调方法供Spring调用,并在XML中配置init-method属性,例如
1 <bean id="chinese" class="spi.Chinese" init-method="init">
只要在bean中实现init()方法,就能在bean被设置完属性之后调用,执行特定方法。
- 二是让bean实现InitialializingBean接口,并实现接口的 afterPropertiesSet()方法
这种方式不要求使用init-method属性,但是这对bean类具有侵入性,不推荐。
6.2Bean销毁之前的行为
要让Bean被销毁之前执行特定行为,也可以类似上面那样用两种方式实现,
一是使用destroy-method属性
二是实现DisposableBean接口,并实现destroy()方法
另外,对于非web应用,如果希望在关闭应用前先关闭容器实例(即ApplicationContext实例),则可以先在JVM中注册一个钩子,程序退出时就会执行回调函数,优雅地关闭容器实例,例如,
1 public static void test5() { 2 AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); 3 Person p = ctx.getBean("chinese", Person.class); 4 p.useAxe(); 5 ctx.registerShutdownHook(); 6 }
6.3协调作用域不同步的Bean
如果在一个singleton类型的bean中注入一个prototype类型的bean时候,会发现有个问题,在整个应用程序生命周期,singleton bean只会被初始化一次,然而prototype bean每次都会重新创建并初始化,那么意味着singleton中的依赖注入并不是最新的。
要解决这个问题,Spring使用“方法注入”,具体方式为:
将调用bean(通常是singleton)定义为抽象类,定义一个抽象方法,用来返回一个对象,这个对象将用来对前面的bean注入值
在调用bean中使用 <lookup-method>子元素替代<property>元素,可以使得每次调用bean时候,都会进行一次注入。
bean定义如下
1 class abstract class Chinese implements Person { 2 private Dog dog; 3 public abstract Dog getDog{} 4 ... 5 }
XML配置
1 <bean id="chinese" class="spi.Chinese"> 2 <lookup-method name="getDog" bean="gunDog" /> 3 </bean> 4 <bean id="gunDog" class="spi.GunDog" scope="prototype" />
对于上面的配置,Spring会做负责生成Chinese抽象类的子类,并重写getDog()方法,通常会这样写,
1 public Dog getDog(){ 2 //获取ctx 3 ... 4 return ctx.getBean("gunDog"); 5 }
这样就强行要求singleton类型的bean,每次获取Dog属性时候,都进行一次依赖注入。
深入了解Spring中的容器