1.创建Bean的3种方式

1.1使用构造器创建bean实例

这是最常见的方式,如果不采用构造注入,bean类需要有默认构造函数。如果采用构造注入,则需要配置xml文件的<constructor-arg>

1.2使用静态工厂方法创建bean

最典型的工厂方法如

 package spi;

 public class PersonFactory {
public static Person getPerson(String arg) {
if (arg.equalsIgnoreCase("Chinese")) {
return new Chinese();
} else {
return new American();
}
}
}

如果在Spring容器中配置一个bean,bean的实例希望由上的静态工厂方法反回,则可以在bean中使用 factory-method来指定工厂方法,并用<constructor-arg>指定参数。

 <bean id="chinese" class="spi.PersonFactory" factory-method="getPerson">
  <constructor-arg value="chinese" />
</bean>

1.3实例化工厂方法创建bean

这与上面的使用静态工厂方法创建bean高度相似,区别是这里是需要先创建工厂的实例。工厂方法如下

 package spi;

 public class PersonFactory {
public Person getPerson(String arg) {
if (arg.equalsIgnoreCase("Chinese")) {
return new Chinese();
} else {
return new American();
}
}
}

在xml配置中,除了使用factory-method来指定bean的实例化工厂方法外,还需要使用factory-bean指定工厂实例的bean

 <bean id="chinese" class="spi.PersonFactory" factory-method="getPerson" factory-bean="personFactory">
  <constructor-arg value="chinese" />
</bean>
<bean id="personFactory" class="spi.PersonFactory" />

2.bean的继承

可以在Spring中使用abstract属性将一个bean定义一个模板配置,这叫做一个抽象bean。抽象bean不能被实例化,只是为了降低配置文件的冗余而存在的,只能由子bean继承,子类则使用parent属性指定父类bean。下面是一个例子,

     <bean id="personTemplate" abstract="true">
<property name="name" value="zhangsan" />
<property name="axe" value="stoneAxe" />
</bean> <bean id="chinesePerson" parent="personTemplate">
<property name="axe" value="steelAxe" />
</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调用,例如下面,

 package spi;

 import java.lang.reflect.Field;

 import org.springframework.beans.factory.FactoryBean;

 public class GetField implements FactoryBean<Object>{
private String targetClass;
private String targetField; public String getTargetClass() {
return targetClass;
} public void setTargetClass(String targetClass) {
this.targetClass = targetClass;
} public String getTargetField() {
return targetField;
} public void setTargetField(String targetField) {
this.targetField = targetField;
} @Override
public Object getObject() throws Exception {
// TODO Auto-generated method stub
Class<?> clazz = Class.forName(targetClass);
Field field = clazz.getField(targetField);
return field.get(null);
} @Override
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return Object.class;
} @Override
public boolean isSingleton() {
// TODO Auto-generated method stub
return false;
} }

这个类实现了FactoryBean接口,可以返回任何我们需要的对象,功能非常强大,在Spring中可以做如下配置,

     <bean id="getField" class="spi.GetField">
<property name="targetClass" value="spi.Chinese" />
<property name="targetField" value="axe" />
</bean>

测试代码如下,

     public static void test6() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
System.out.println(ctx.getBean("getField"));
}

程序输出:North

Spring提供的工厂Bean,大多以FactoryBean结尾,用于生产指定类型的bean,工厂bean是Spring的重要工具之一。

4.获取Bean本身id

就像Bean希望获取所在的ApplicationContext实例一样,有时候Bean还希望获取XML中配置的id名称,这种情况需要bean实现BeanNameAware接口,过程与实现ApplicationContextAware类似。并且实现BeanNameAware接口的setBeanName()方法供Spring调用,例如下面,

 public class Chinese implements Person, BeanNameAware {
private String beanName;
@Override
public void setBeanName(String arg0) {
this.beanName = beanName; }
...

这样容器创建bean时候就会调用这个setBeanName()方法,bean就能获取自己的id名称了。例如还可以在Chinese类中添加下面的方法,

     public void info(){
System.out.println("我在XML中的id名称为:"+beanName);
}

5.强制初始化bean

某些情况下,容器中的依赖并不是非常直接,初始化bean时候,如果依赖的bean尚未初始化,有可能会出错,这种情况下我们希望先初始化依赖的bean。

我们可以使用depebds-on属性来强制容器先初始化依赖额bean,例如

 <bean id="chinese" class="spi.Chinese" depends-on="steelAxe" />
<bean id="steelAxe" class="spi.SteelAxe" />

6.容器中Bean的生命周期

对于singleton的bean,Spring可以精确控制生命周期,因此可以在bean的各个阶段指定各种行为。

例如在bean依赖注入之后,或者销毁之前,可以插入指定动作。

6.1依赖关系注入之后的行为

有两种方式可以在Bean属性设置成功之后执行特定行为。

  • 一是使用init-method属性

这种方式要求在bean中定义一个回调方法供Spring调用,并在XML中配置init-method属性,例如

 <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中注册一个钩子,程序退出时就会执行回调函数,优雅地关闭容器实例,例如,

     public static void test5() {
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
Person p = ctx.getBean("chinese", Person.class);
p.useAxe();
ctx.registerShutdownHook();
}

6.3协调作用域不同步的Bean

如果在一个singleton类型的bean中注入一个prototype类型的bean时候,会发现有个问题,在整个应用程序生命周期,singleton bean只会被初始化一次,然而prototype bean每次都会重新创建并初始化,那么意味着singleton中的依赖注入并不是最新的。

要解决这个问题,Spring使用“方法注入”,具体方式为:

将调用bean(通常是singleton)定义为抽象类,定义一个抽象方法,用来返回一个对象,这个对象将用来对前面的bean注入值

在调用bean中使用 <lookup-method>子元素替代<property>元素,可以使得每次调用bean时候,都会进行一次注入。

bean定义如下

 class abstract class Chinese implements Person {
private Dog dog;
public abstract Dog getDog{}
...
}

XML配置

 <bean id="chinese" class="spi.Chinese">
<lookup-method name="getDog" bean="gunDog" />
</bean>
<bean id="gunDog" class="spi.GunDog" scope="prototype" />

对于上面的配置,Spring会做负责生成Chinese抽象类的子类,并重写getDog()方法,通常会这样写,

 public Dog getDog(){
//获取ctx
...
return ctx.getBean("gunDog");
}

这样就强行要求singleton类型的bean,每次获取Dog属性时候,都进行一次依赖注入。

7.获取其他bean的属性值

获取bean属性值,即调用getter方法。Spring提供一个工厂Bean : PropertyPathFactoryBean专门用来调用getter方法获取属性值。

Spring的工厂Bean很强大,大多以*FactoryBean名称结尾,每一种工厂Bean可以生产特定产品,PropertyPathFactoryBean则是专门用来获取属性值的。

要使用PropertyPathFactoryBean,直接将它配置成一个XML中的bean即可,同时需要配置bean的targetBeanName和propertyPath属性,下面是一个例子,

     <bean id="chinese" class="spi.Chinese" destroy-method="close">
<property name="age" value="30" />
<property name="son">
<bean class="spi.Son">
<property name="age" value="11" />
</bean>
</property>
</bean>
<!-- 将指定Bean实例的getter方法返回值定义成son1 bean -->
<bean id="son1" class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
<!-- 指定son1 bean来自哪个目标bean的getter方法 -->
<property name="targetBeanName" value="chinese" />
<!-- 指定目标bean的哪个getter方法, axe代表getSon() -->
<property name="propertyPath" value="son" />
</bean>

targetBeanName属性用来指定将要获取哪个bean的属性,propertyPath用来指定要执行目标bean的哪个getter方法。

上面涉及一个Son类代码如下,

 package spi;

 public class Son {
private int age; public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
}
public String toString(){
return "Son[age="+this.age+"]";
}
}

测试代码如下,

 System.out.println("系统获取son1: "+ctx.getBean("son1"));

将会得到如下结果,

系统获取son1: Son[age=11]

获取到的bean属性值还可以用来注入另一个bean,下面是一个例子,

     <bean id="son2" class="spi.Son">
<property name="age">
<!-- 使用嵌套bean为setAge()方法指定参数 -->
<!-- 以下是访问指定bean的getter方法的简单方式,
chinese.son.age代表获取chinese.getSon().getAge(), 也就是前面的son1-->
<bean id="chinese.son.age" class="org.springframework.beans.factory.config.PropertyPathFactoryBean" />
</property>
</bean>

测试代码,

 System.out.println("系统获取son2: "+ctx.getBean("son2"));

执行结果,

 系统获取son2: Son[age=11]

另外,propertyPath所指定的getter方法,也可以是一个复合方法(即属性的属性),像下面这样,

     <bean id="theAge" class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
<property name="targetBeanName" value="chinese" />
<!-- 这里的son.age是一个复合属性,表示chinese.getSon().getAge() -->
<property name="propertyPath" value="son.age" />
</bean>

测试代码,

 System.out.println("系统获取theAge: "+ctx.getBean("theAge"));

执行结果,

系统获取theAge: 11

同时,目标bean也可以是嵌入的一个bean,不过这时候不是使用targetBeanName属性来指定,而是targetObject,例如这样,

     <bean id="theAge2" class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
<property name="targetObject">
<bean class="spi.American">
<property name="age" value="30" />
</bean>
</property>
<property name="propertyPath" value="age" />
</bean>

测试代码,

 System.out.println("系统获取theAge2: "+ctx.getBean("theAge2"));

执行结果,

 系统获取theAge2: 30

另外,对于获取其他bean属性的工厂Bean配置,还有一种简化配置如下,

 <util:property-path id="son1" path="person.son" />

不过这种配置需要导入util:命名空间。

8.获取Field值

获取Field分为两种情况,第一种是静态Field,第二种是对象实例的Field值。

Spring定义了专门的工厂Bean:FieldRetrievingFactoryBean来获取Field值。在XML中配置一个FieldRetrievingFactoryBean类型的bean即可。

对于静态Field情形,需要在XML中设置FieldRetrievingFactoryBean bean的 targetClass(即目标类)和targetFiled(即目标字段)。

对于实例对象的Field,需要指定targetObject(目标对象)和targetField(目标字段)。

对于第二种情况,其实编程中意义不大,因为通常定义在类中的对象实例都是private的,FieldRetrievingFactoryBean无法直接获取,我们使用PropertyPathFactoryBean调用public的getter方法即可。

对于第一种情况,典型用法如下,

     <bean id="theAge3" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<property name="targetClass" value="java.sql.Connection" />
<property name="targetField" value="TRANSACTION_SERIALIZABLE" />
</bean>

FieldRetrievingFactoryBean有个setStaticField()方法用来指定目标类和目标Field,因此上面的配置又可以简写为,

     <bean id="theAge4" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<property name="staticField" value="java.sql.Connection.TRANSACTION_SERIALIZABLE" />
</bean>

FieldRetrievingFactoryBean返回的bean也可以用来注入其他bean,

     <bean id="son3" class="spi.Son">
<property name="age">
<bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" />
</property>
</bean>

上面三段配置的测试代码如下,

         System.out.println("系统获取theAge3: "+ctx.getBean("theAge3"));
System.out.println("系统获取theAge4: "+ctx.getBean("theAge4"));
System.out.println("系统获取son3: "+ctx.getBean("son3"));

测试结果,

 系统获取theAge3: 8
系统获取theAge4: 8
系统获取son3: Son[age=8]

FieldRetrievingFactoryBean也支持命名空间的简写,即

<util:constrant static-field="java.sql.Connection.TRANSACTION_SERIALIZABLE" />

9.获取方法返回值

Spring使用工厂Bean:MethodInvokingFactoryBean来执行bean中的普通方法,如果有返回值,将被赋值给xml中定义的bean

执行普通方法也分两种情况,一种是静态类的普通方法,一种是普通对象的方法,

对于两种情况,都将MethodInvokingFactoryBean定义为XML中一个普通bean,且设置相似的参数。

targetClass/targetObject 用来设置目标类或者目标对象

targetMethod用来设置目标方法

arguments用来设置执行普通方法所需要的参数

一个典型的用法如下,

     <!-- 配置
JFrame win = new JFrame("我的窗口");
win.setVisible(true);
-->
<bean id="win" class="javax.swing.JFrame">
<constructor-arg value="我的窗口" type="java.lang.String" />
<property name="visible" value="true" />
</bean>
<!-- 配置
JTextArea jta = JTextArea(7,40);
-->
<bean id="jta" class="javax.swing.JTextArea">
<constructor-arg value="7" type="int" />
<constructor-arg value="40" type="int" />
</bean> <!-- 配置
win.add(new JScrollPane(jta));
使用MethodInvokingFactoryBean驱动Spring调用普通方法
-->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="win" />
<property name="targetMethod" value="add" />
<property name="arguments">
<list>
<bean class="javax.swing.JScrollPane">
<constructor-arg ref="jta" />
</bean>
</list>
</property>
</bean>

上面使用纯XML配置的方法,相当于执行了下面的java代码,

 JFrame win = new JFrame("我的窗口");
win.setVisible(true);
JTextArea jta = JTextArea(7,40);
win.add(new JScrollPane(jta));

通过上面的几点发现,Spring框架就是通过XML配置来执行java代码,因此几乎可以把所有java代码都放在Spring配置中管理,归纳一下:

  • 调用构造器创建对象,用<bean ..>元素
  • 调用setter方法,用<property..>元素
  • 调用getter方法,用工厂Bean  PropertyPathFactoryBean
  • 调用普通方法,用工厂Bean  MethodInvokingFactoryBean
  • 获取Field的值,用工厂Bean FieldRetrievingFactoryBean

深入了解Spring中的容器的更多相关文章

  1. Spring 中 IoC 容器简介

    IoC 是一种通过描述来生成或者获取对象的技术,可以说 Spring 是一种基于 IoC 容器编程的框架 在一个系统中可以生成各种对象,并且这些对象都需要进行管理.为了描述这些对象关系,我们需要一个容 ...

  2. spring中IOC容器注册和获取bean的实例

    spring中常用的功能主要的是ioc和aop,此处主要说明下,实例注册和使用的方法,此为学习后的笔记记录总结 1.使用xml文件配置 在idea中创建maven工程,然后创建实例Person,然后在 ...

  3. Spring中的容器

    1.Spring容器 Spring容器最基本的接口就是BeanFactory, 负责配置,创建和管理bean.我们通常不直接使用BeanFactory接口,而是使用其子接口ApplicationCon ...

  4. spring中获取容器中的Bean为什么前转成接口而不是实现类

    简单介绍一下上下文,userService是服务层接口有一个save方法,userServiceImpl是该接口的实现类重写了save方法. applicationContext.xml如图: 后台代 ...

  5. 半夜思考之查漏补缺, Spring 中的容器后处理器

    之前学 Spring 的时候 , 还没听过容器后处理器 , 但是一旦写出来 , 就会觉得似曾相识 . 容器配置器通常用于对 Spring 容器进行处理 , 并且总是在容器实例化任何其他 Bean 之前 ...

  6. Spring中Ioc容器的注入方式

    1 通过setter方法注入 bean类: package com.test; public class UserServiceImplement implements IUserService { ...

  7. 挖坟之Spring.NET IOC容器初始化

    因查找ht项目中一个久未解决spring内部异常,翻了一段时间源码.以此文总结springIOC,容器初始化过程. 语言背景是C#.网上有一些基于java的spring源码分析文档,大而乱,乱而不全, ...

  8. Spring之IOC容器

    在前面博客中介绍什么是依赖注入时有提到:依赖注入是组件之间依赖关系由容器在运行期决定,即由容器动态的将某个依赖关系注入到组件之中.那什么是容器?既然Spring框架实现了IOC,那Spring中的容器 ...

  9. Spring中常见的设计模式——单例模式

    一.单例模式的应用场景 单例模式(singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点.J2EE中的ServletContext,ServletCon ...

随机推荐

  1. 初中级DBA必需要学会的9个Linux网络命令,看看你有哪些还没用过

    笔者不久前写了一篇文章<做DBA必须学会,不会会死的11个Linux基本命令>,博文地址为:http://blog.csdn.net/ljunjie82/article/details/4 ...

  2. java基础开发—jstl标签库

    在DRP项目中.接触到了JSTL标签库. 在未使用Jstl之前,我们使用JSP脚本实现一些声明或是表达式任务,做一些业务相关操作时,须要在页面中嵌入大量的java代码.在DRP项目开发前期.使用jsp ...

  3. 并查集图冲突hdu1272

    还是属于并查集的变形 两个点仅仅有一条路径连通 给出的两个点事先都是属于两个集合的 须要给出的着条边构成一个集合 算法复杂度还是挺高的 每一个我都循环了100000次 set2数组没清空 wrong了 ...

  4. UML的基本图(二)

     Both sequence diagrams and communication diagrams are kinds of interaction diagrams. An interacti ...

  5. HDU Distinct Values

    /* 一开始想到的是 对于每个区间操作 先按左端点排序(包含的区间 留这打的区间) 我们维护pos表示 a数组找到了哪 对于当前这个区间 只需要找 pos--r这个区间 用set维护能用的数 没放到a ...

  6. php手机号码验证正则表达式

    移动:134.135.136.137.138.139.150.151.152.157.158.159.182.183.184.187.188.178(4G).147(上网卡): 联通:130.131. ...

  7. O - Masha and Bears

    Problem description A family consisting of father bear, mother bear and son bear owns three cars. Fa ...

  8. sublime3 install python3

    链接地址:https://blog.csdn.net/Ti__iT/article/details/78830040

  9. wppay免登录付费查看隐藏内容/付费资源下载

    WPPAY是一款模板兔开发的免登录的付费查看内容/付费下载资源WordPress插件,WPPAY不需要用户注册登录即可支付查看隐藏内容,把整个流程做到极简.发布文章时要隐藏的内容可以利用短代码: [w ...

  10. Vue组件的三种调用方式

    最近在写fj-service-system的时候,遇到了一些问题.那就是我有些组件,比如Dialog.Message这样的组件,是引入三方组件库,比如element-ui这样的,还是自己实现一个?虽然 ...