深入了解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属性时候,都进行一次依赖注入。

时间: 2024-12-26 06:22:01

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

Spring中父子容器的实现实例

Spring中父子容器的实现实例Spring的父子容器可以通过ConfigurableApplicationContext或ConfigurableBeanFactory来实现,这两个接口中分别有setParent及setParentBeanFactory方法,可以与当前的子容器进行父子容器关联,这个时候子容器就可以引用父容器中的bean,但是父容器是不能够引用子容器中的bean的,并且各个子容器中定义的bean是互不可见的,这样也可以避免因为不同的插件定义了相同的bean而带来的麻烦.应用场景

Spring 中 IoC 容器简介

IoC 是一种通过描述来生成或者获取对象的技术,可以说 Spring 是一种基于 IoC 容器编程的框架 在一个系统中可以生成各种对象,并且这些对象都需要进行管理.为了描述这些对象关系,我们需要一个容器.在 Spring 中把每一个需要管理的对象称为 Spring Bean ,而管理这些 Bean 的容器就被称为 Spring IoC 容器. IoC 容器需要具备两个基本的功能: 通过描述管理 Bean ,包括发布和获取 Bean 通过描述完成 Bean 之间的依赖关系 介绍 Spring Io

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

之前学 Spring 的时候 , 还没听过容器后处理器 , 但是一旦写出来 , 就会觉得似曾相识 . 容器配置器通常用于对 Spring 容器进行处理 , 并且总是在容器实例化任何其他 Bean 之前 , 读取配置文件中的元数据 , 并有可能修改这些数据 . Spring 提供了如下几个常用的容器后处理器 : PropertyPlaceholderConfigurer : 属性占位符配置器 PropertyOverrideConfigurer : 重写占位符配置器 CustomAutowireC

Spring中Ioc容器的注入方式

1 通过setter方法注入 bean类: package com.test; public class UserServiceImplement implements IUserService { private IUserDao user; public IUserDao getUser() { return user; } public void setUser(IUserDao user) { this.user = user; } public void saveUser() { us

Spring中IOC容器对数组,集合的相关bean的装配

<bean id="department" class="com.pojo.Department"> <property name="name" value="人事部"></property> <!-- 数组 --> <property name="empName"> <list> <value>张三</value&g

Spring之IOC容器

在前面博客中介绍什么是依赖注入时有提到:依赖注入是组件之间依赖关系由容器在运行期决定,即由容器动态的将某个依赖关系注入到组件之中.那什么是容器?既然Spring框架实现了IOC,那Spring中的容器是什么呢? 一.容器介绍在日常生活中容器是指用以容纳物料并以壳体为主的基本装置,它是用来盛放东西的.在编程中容器是用来存储和组织其他对象的对象,首先要确定容器也是对象,也可以当做bean,只是这个对象是用来存储和组织其他对象,那其他对象是什么呢?其他对象其实就是bean对象,这也是面向对象编程的一种

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

一.单例模式的应用场景 单例模式(singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点.J2EE中的ServletContext,ServletContextConfig等:Spring中的ApplicationContext.数据库连接池等. 二.饿汉式单例模式 饿汉式单例模式在类加载的时候就立即初始化,并且创建单例对象.它是绝对的线程安全.在线程还没出现以前就实现了,不可能存在访问安全问题. 优点:没有增加任何锁,执行效率高,用户体验比懒汉式

Spring中基于Java的容器配置(二)

使用@Configuration注解 @Configuration注解是一个类级别的注解,表明该对象是用来指定Bean的定义的.@Configuration注解的类通过@Bean注解的方法来声明Bean.通过调用注解了@Bean方法的返回的Bean可以用来构建Bean之间的相互依赖关系,可以通过前文来了解其基本概念. 注入inter-bean依赖 当@Bean方法依赖于其他的Bean的时候,可以通过在另一个方法中调用即可. @Configuration public class AppConfi

采用Spring实现在容器启动时把用ConcurrentHashMap实现的并发缓存加载到ServletContext中

1.ConstantDataDTO.java,一定要重写hashcode和equals方法 import java.io.Serializable; import java.util.Date; /** * ConstantDataDTO.java * * @author steveguoshao * @created 2014年07月10日 下午6:15:55 * @version 1.0 */ public class ConstantDataDTO implements Serializa