欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详解常用的Spring Bean扩展接口

程序员文章站 2024-02-24 18:18:22
前言 spring是一款非常强大的框架,可以说是几乎所有的企业级java项目使用了spring,而bean又是spring框架的核心。 spring框架运用了非常多的设...

前言

spring是一款非常强大的框架,可以说是几乎所有的企业级java项目使用了spring,而bean又是spring框架的核心。

spring框架运用了非常多的设计模式,从整体上看,它的设计严格遵循了ocp----开闭原则,即:

1、保证对修改关闭,即外部无法修改spring整个运作的流程

2、提供对扩展开放,即可以通过继承、实现spring提供的众多抽象类与接口来改变类加载的行为

开卷有益,阅读spring源码(无需每个类都看得很细,大体流程能梳理出来即可)对于个人水平的提升是帮助非常大的,同时也能在工作中即使发现和解决一些不常见的spring问题。

不过,本文的目的不是整理spring的流程,而是通过介绍一些常用的spring bean工具类,来让我们可以更好地使用spring提供给开发者的多种特性,下面让我们开始吧。

initialingbean和disposablebean

initialingbean是一个接口,提供了一个唯一的方法afterpropertiesset()。

disposablebean也是一个接口,提供了一个唯一的方法destory()。

这两个接口是一组的,功能类似,因此放在一起:前者顾名思义在bean属性都设置完毕后调用afterpropertiesset()方法做一些初始化的工作,后者在bean生命周期结束前调用destory()方法做一些收尾工作。下面看一下例子,为了能明确地知道afterpropertiesset()方法的调用时机,加上一个属性,给属性set方法,在set方法中打印一些内容:

/**
 * @author 五月的仓颉 http://www.cnblogs.com/xrq730/p/5721366.html
 */
public class lifecyclebean implements initializingbean, disposablebean
{
  @suppresswarnings("unused")
  private string  lifecyclebeanname;

  public void setlifecyclebeanname(string lifecyclebeanname)
  {
    system.out.println("enter lifecyclebean.setlifecyclebeanname(), lifecyclebeanname = " + lifecyclebeanname);
    this.lifecyclebeanname = lifecyclebeanname;
  }

  public void destroy() throws exception
  {
    system.out.println("enter lifecyclebean.destroy()");
  }

  public void afterpropertiesset() throws exception
  {
    system.out.println("enter lifecyclebean.afterpropertiesset()");
  }

  public void beanstart()
  {
    system.out.println("enter lifecyclebean.beanstart()");
  }
  public void beanend()
  {
    system.out.println("enter lifecyclebean.beanend()");
  }
}

配置一个spring.xml:

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
  xsi:schemalocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
  
  <bean id="lifecyclebean" class="org.xrq.bean.lifecycle.lifecyclebean">
    <property name="lifecyclebeanname" value="lifecyclebean" />
  </bean>
</beans>

启动spring容器,lifecyclebean执行的结果为:

enter lifecyclebean.setlifecyclebeanname(), lifecyclebeanname = lifecyclebean
enter lifecyclebean.afterpropertiesset()
enter lifecyclebean.beanstart()
enter lifecyclebean.destroy()
enter lifecyclebean.beanend()

执行结果和我们想的一样,afterpropertiesset()方法就如同它的名字所表示的那样,是bean的属性都被设置完毕之后,才会调用

关于这两个接口,我总结几点:

1、initializingbean接口、disposable接口可以和init-method、destory-method配合使用,接口执行顺序优先于配置

2、initializingbean接口、disposable接口底层使用类型强转.方法名()进行直接方法调用,init-method、destory-method底层使用反射,前者和spring耦合程度更高但效率高,后者解除了和spring之间的耦合但是效率低,使用哪个看个人喜好

3、afterpropertiesset()方法是在bean的属性设置之后才会进行调用,某个bean的afterpropertiesset()方法执行完毕才会执行下一个bean的afterpropertiesset()方法,因此不建议在afterpropertiesset()方法中写处理时间太长的方法

beannameaware、applicationcontextaware和beanfactoryaware

这三个接口放在一起写,是因为它们是一组的,作用相似。

"aware"的意思是"感知到的",那么这三个接口的意思也不难理解:

1、实现beannameaware接口的bean,在bean加载的过程中可以获取到该bean的id

2、实现applicationcontextaware接口的bean,在bean加载的过程中可以获取到spring的applicationcontext,这个尤其重要,applicationcontext是spring应用上下文,从applicationcontext中可以获取包括任意的bean在内的大量spring容器内容和信息

3、实现beanfactoryaware接口的bean,在bean加载的过程中可以获取到加载该bean的beanfactory

看一下例子:

/**
 * @author 五月的仓颉 http://www.cnblogs.com/xrq730/p/5721366.html
 */
public class awarebean implements beannameaware, beanfactoryaware, applicationcontextaware
{
  private string           beanname;

  private applicationcontext    applicationcontext;

  private beanfactory        beanfactory;

  public void setbeanname(string beanname)
  {
    system.out.println("enter awarebean.setbeanname(), beanname = " + beanname + "\n");
    this.beanname = beanname;
  }

  public void setapplicationcontext(applicationcontext applicationcontext) throws beansexception
  {
    system.out.println("enter awarebean.setapplicationcontext(), applicationcontext = " + applicationcontext + "\n");
    this.applicationcontext = applicationcontext;
  }

  public void setbeanfactory(beanfactory beanfactory) throws beansexception
  {
    system.out.println("enter awarebean.setbeanfactory(), beanfactory = " + beanfactory + "\n");
    this.beanfactory = beanfactory;
  }
}

配置一个spring.xml:

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
xsi:schemalocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
<bean id="awarebean" class="org.xrq.bean.aware.awarebean" />
</beans>

启动spring容器后的执行结果为:

enter awarebean.setbeanname(), beanname = awarebean

enter awarebean.setbeanfactory(), beanfactory = org.springframework.beans.factory.support.defaultlistablebeanfactory@2747fda0: defining beans [awarebean,org.springframework.context.annotation.internalconfigurationannotationprocessor,org.springframework.context.annotation.internalautowiredannotationprocessor,org.springframework.context.annotation.internalrequiredannotationprocessor,org.springframework.context.annotation.internalcommonannotationprocessor,org.springframework.context.annotation.configurationclasspostprocessor.importawareprocessor,org.springframework.context.annotation.configurationclasspostprocessor.enhancedconfigurationprocessor]; root of factory hierarchy

enter awarebean.setapplicationcontext(), applicationcontext = org.springframework.context.support.genericapplicationcontext@5514cd80: startup date [mon aug 08 19:23:30 cst 2016]; root of context hierarchy

关于这三个接口以及上面的打印信息,总结几点:

1、如果你的beanname、applicationcontext、beanfactory有用,那么就自己定义一个变量将它们保存下来,如果没用,那么只需要实现setxxx()方法,用一下spring注入进来的参数即可

2、如果bean同时还实现了initializingbean,容器会保证beanname、applicationcontext和beanfactory在调用afterpropertiesset()方法被注入

factorybean

factorybean在spring中是非常有用的,使用eclipse/myeclipse的朋友可以对factorybean使用ctrl+t查看一下,factorybean这个接口在spring容器中有大量的子实现。

传统的spring容器加载一个bean的整个过程,都是由spring控制的,换句话说,开发者除了设置bean相关属性之外,是没有太多的自主权的。factorybean改变了这一点,开发者可以个性化地定制自己想要实例化出来的bean,方法就是实现factorybean接口。

看一下代码例子,为了讲清楚factorybean,内容相对多一些,首先定义一个接口animal:

public interface animal
{
  public void move();
}

定义两个实现类monkey和tiger:

public class monkey implements animal
{
  public void move()
  {
    system.out.println("monkey move!");
  }
}
public class tiger implements animal
{
  public void move()
  {
    system.out.println("tiger move!");
  }
}

写一个实现类,实现factorybean接口:

/**
 * @author 五月的仓颉 http://www.cnblogs.com/xrq730/p/5721366.html
 */
public class animalfactorybean implements factorybean<animal>
{
  private string  animal;

  public animal getobject() throws exception
  {
    if ("monkey".equals(animal))
    {
      return new monkey();
    }
    else if ("tiger".equals(animal))
    {
      return new tiger();
    }
    else
    {
      return null;
    }
  }

  public class<?> getobjecttype()
  {
    return animal.class;
  }

  public boolean issingleton()
  {
    return true;
  }
  public void setanimal(string animal)
  {
    this.animal = animal;
  }
}

配置一个spring.xml,注入属性tiger:

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
  xsi:schemalocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
  
  <bean id="animal" class="org.xrq.bean.factory.animalfactorybean">
    <property name="animal" value="tiger"/>
  </bean>  
</beans>

写一个junit的测试类:

@runwith(springjunit4classrunner.class)
@contextconfiguration(locations = {
    "classpath*:spring.xml",
})
public class basetest
{
  @resource
  private animal  animal;
  
  @test
  public void aa()
  {
    animal.move();
  }
}

查看一下运行结果:

tiger move!

看到最后得到的并不是factorybean本身,而是factorybean的泛型对象,这就是factorybean的作用。factorybean的几个方法:

1、getobject()方法是最重要的,控制bean的实例化过程

2、getobjecttype()方法获取接口返回的实例的class

3、issingleton()方法获取该bean是否为一个单例的bean

像我这段代码的功能就是传入一个string类型的参数,可以动态控制生成出来的是接口的哪种子类。有了factorybean,同样的我们也可以灵活地操控bean的生成。

beanpostprocessor

之前的initializingbean、disposablebean、factorybean包括init-method和destory-method,针对的都是某个bean控制其初始化的操作,而似乎没有一种办法可以针对每个bean的生成前后做一些逻辑操作,postprocessor则帮助我们做到了这一点,先看一个简单的beanpostprocessor。

网上有一张图画了bean生命周期的过程,画得挺好,原图出处:

 详解常用的Spring Bean扩展接口

beanpostprocess接口有两个方法,都可以见名知意:

1、postprocessbeforeinitialization:在初始化bean之前

2、postprocessafterinitialization:在初始化bean之后

值得注意的是,这两个方法是有返回值的,不要返回null,否则getbean的时候拿不到对象。

写一段测试代码,首先定义一个普通的bean,为了后面能区分,给bean加一个属性:

public class commonbean
{
  private string commonname;
  public void setcommonname(string commonname)
  {
    this.commonname = commonname;
  }
  public void initmethod()
  {
    system.out.println("enter commonbean.initmethod(), commonname = " + commonname);
  }
}

定义一个postprocess,实现beanpostprocess接口:

/**
 * @author 五月的仓颉 http://www.cnblogs.com/xrq730/p/5721366.html
 */
public class postprocessorbean implements beanpostprocessor
{
  public object postprocessafterinitialization(object bean, string beanname) throws beansexception
  {
    system.out.println("enter processorbean.postprocessafterinitialization()\n");
    return bean;
  }
  public object postprocessbeforeinitialization(object bean, string beanname) throws beansexception
  {
    system.out.println("enter processorbean.postprocessbeforeinitialization()");
    return bean;
  }
}

配置一个spring.xml,给commonbean的commonname赋予不同的值以区分:

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
  xsi:schemalocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">

  <bean id="common0" class="org.xrq.bean.common.commonbean" init-method="initmethod">
    <property name="commonname" value="common0"/>
  </bean>

  <bean id="common1" class="org.xrq.bean.common.commonbean" init-method="initmethod">
    <property name="commonname" value="common1"/>
  </bean>
  <bean id="postprocessorbean" class="org.xrq.bean.processor.postprocessorbean" />
</beans>

运行一个spring容器, 初始化结果为:

enter processorbean.postprocessbeforeinitialization()
enter commonbean.initmethod(), commonname = common0
enter processorbean.postprocessafterinitialization()

enter processorbean.postprocessbeforeinitialization()
enter commonbean.initmethod(), commonname = common1
enter processorbean.postprocessafterinitialization()
enter processorbean.postprocessbeforeinitialization()
enter processorbean.postprocessafterinitialization()

看到每个bean初始化前后都会分别执行postprocessorbeforeinitiallization()方法与postprocessorafterinitialization()方法,最后两行出现原因是,postprocessorbean本身也是一个bean。

beanfactorypostprocessor

接下来看另外一个postprocessor----beanfactorypostprocessor。

spring允许在bean创建之前,读取bean的元属性,并根据自己的需求对元属性进行改变,比如将bean的scope从singleton改变为prototype,最典型的应用应当是propertyplaceholderconfigurer,替换xml文件中的占位符,替换为properties文件中相应的key对应的value,这将会在下篇文章中专门讲解propertyplaceholderconfigurer的作用及其原理。

beanfactorypostprocessor就可以帮助我们实现上述的功能,下面来看一下beanfactorypostprocessor的使用,定义一个beanfactorypostprocessor的实现类:

/**
 * @author 五月的仓颉 http://www.cnblogs.com/xrq730/p/5721366.html
 */
public class factorypostprocessorbean implements beanfactorypostprocessor
{
  public void postprocessbeanfactory(configurablelistablebeanfactory configurablelistablebeanfactory)
      throws beansexception
  {
    system.out.println("enter factorypostprocessorbean.postprocessbeanfactory()\n");
  }
}

spring.xml里面配置一下这个bean,就不写了,运行一下spring容器,结果为:

enter factorypostprocessorbean.postprocessbeanfactory()

enter processorbean.postprocessbeforeinitialization()
enter commonbean.initmethod(), commonname = common0
enter processorbean.postprocessafterinitialization()

enter processorbean.postprocessbeforeinitialization()
enter commonbean.initmethod(), commonname = common1
enter processorbean.postprocessafterinitialization()
enter processorbean.postprocessbeforeinitialization()
enter processorbean.postprocessafterinitialization()

从执行结果中可以看出两点:

1、beanfactorypostprocessor的执行优先级高于beanpostprocessor

2、beanfactorypostprocessor的postprocessbeanfactory()方法只会执行一次

注意到postprocessbeanfactory方法是带了参数configurablelistablebeanfactory的,这就和我之前说的可以使用beanfactorypostprocessor来改变bean的属性相对应起来了。configurablelistablebeanfactory功能非常丰富,最基本的,它携带了每个bean的基本信息,比如我简单写一段代码:

/**
 * @author 五月的仓颉 http://www.cnblogs.com/xrq730/p/5721366.html
 */
public void postprocessbeanfactory(configurablelistablebeanfactory configurablelistablebeanfactory)
      throws beansexception
{
  beandefinition beandefinition = configurablelistablebeanfactory.getbeandefinition("common0");
  mutablepropertyvalues beanproperty = beandefinition.getpropertyvalues();
  system.out.println("scope before change:" + beandefinition.getscope());
  beandefinition.setscope("singleton");
  system.out.println("scope after change:" + beandefinition.getscope());
  system.out.println("beanproperty:" + beanproperty);
}

看一下执行结果:

scope before change:
scope after change:singleton
beanproperty:propertyvalues: length=1; bean property 'commonname'

这样就获取了bean的生命周期以及重新设置了bean的生命周期。configurablelistablebeanfactory还有很多的功能,比如添加beanpostprocessor,可以自己去查看。

instantiationawarebeanpostprocessor

最后写一个叫做instantiationawarebeanpostprocessor的postprocessor。

instantiationawarebeanpostprocessor又代表了spring的另外一段生命周期:实例化。先区别一下spring bean的实例化和初始化两个阶段的主要作用:

1、实例化----实例化的过程是一个创建bean的过程,即调用bean的构造函数,单例的bean放入单例池中

2、初始化----初始化的过程是一个赋值的过程,即调用bean的setter,设置bean的属性

之前的beanpostprocessor作用于过程(2)前后,现在的instantiationawarebeanpostprocessor则作用于过程(1)前后,看一下代码,给前面的commonbean加上构造函数:

public class commonbean
{
  public commonbean()
  {
    system.out.println("enter commonbean's constructor");
  }

  private string commonname;

  public void setcommonname(string commonname)
  {
    system.out.println("enter commonbean.setcommonname(), commonname = " + commonname);
    this.commonname = commonname;
  }
  public void initmethod()
  {
    system.out.println("enter commonbean.initmethod(), commonname = " + commonname);
  }
}

实现instantiationawarebeanpostprocessor接口:

/**
 * @author 五月的仓颉 http://www.cnblogs.com/xrq730/p/5721366.html
 */
public class instantiationawarebeanpostprocessorbean implements instantiationawarebeanpostprocessor
{
  public object postprocessafterinitialization(object bean, string beanname) throws beansexception
  {
    system.out.println("enter instantiationawarebeanpostprocessorbean.postprocessafterinitialization()");
    return bean;
  }

  public object postprocessbeforeinitialization(object bean, string beanname) throws beansexception
  {
    system.out.println("enter instantiationawarebeanpostprocessorbean.postprocessbeforeinitialization()");
    return bean;
  }

  public boolean postprocessafterinstantiation(object bean, string beanname) throws beansexception
  {
    system.out.println("enter instantiationawarebeanpostprocessorbean.postprocessafterinstantiation()");
    return true;
  }

  public object postprocessbeforeinstantiation(class<?> bean, string beanname) throws beansexception
  {
    system.out.println("enter instantiationawarebeanpostprocessorbean.postprocessbeforeinstantiation()");
    return null;
  }
  public propertyvalues postprocesspropertyvalues(propertyvalues pvs, propertydescriptor[] pd, object bean,
      string beanname) throws beansexception
  {
    return pvs;
  }
}

配置一下spring.xml:

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
  xsi:schemalocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
  
  <bean id="common" class="org.xrq.bean.common.commonbean">
    <property name="commonname" value="common"/>
  </bean>
  
  <bean class="org.xrq.bean.processor.instantiationawarebeanpostprocessorbean" />
</beans>

启动容器,观察一下运行结果为:

enter instantiationawarebeanpostprocessorbean.postprocessbeforeinstantiation()
enter commonbean's constructor
enter instantiationawarebeanpostprocessorbean.postprocessafterinstantiation()
enter commonbean.setcommonname(), commonname = common
enter instantiationawarebeanpostprocessorbean.postprocessbeforeinitialization()
enter instantiationawarebeanpostprocessorbean.postprocessafterinitialization()
enter instantiationawarebeanpostprocessorbean.postprocessafterinstantiation()
enter instantiationawarebeanpostprocessorbean.postprocessbeforeinitialization()
enter instantiationawarebeanpostprocessorbean.postprocessafterinitialization()

最后三行的运行结果不去关注,看到很明显的,instantiationawarebeanpostprocessor作用的是bean实例化前后,即:

1、bean构造出来之前调用postprocessbeforeinstantiation()方法

2、bean构造出来之后调用postprocessafterinstantiation()方法

不过通常来讲,我们不会直接实现instantiationawarebeanpostprocessor接口,而是会采用继承instantiationawarebeanpostprocessoradapter这个抽象类的方式来使用。

后记

如果只会写个bean,配置在xml文件里面,注入一下,那是最最基础的spring开发者。一个中级、高级的spring开发者,必然会对spring中的多个扩展点有所了解,并利用这些扩展点更好地为项目服务,使得整个代码结构更加地优雅,并且可读性、可维护性更好。

抛砖引玉,本文只是简单地介绍一些常用的spring bean扩展接口以及它们的简单用法,更深入的或者它们一些合适的使用场景,还需要留待网友朋友们自己去探索。 

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!