首頁  >  文章  >  Java  >  關於Spring Bean擴充介面的方法介紹

關於Spring Bean擴充介面的方法介紹

Y2J
Y2J原創
2017-05-11 09:55:001552瀏覽

本篇文章主要介紹了一些常用的Spring Bean擴充介面以及它們的簡單用法,具有很好的參考價值。下面跟著小編一起來看下吧

前言

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生命周期的过程,画得挺好,原图出处:

 

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 &#39;commonName&#39;

这样就获取了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&#39;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&#39;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中的多个扩展点有所了解,并利用这些扩展点更好地为项目服务,使得整个代码结构更加地优雅,并且可读性、可维护性更好。

【相关推荐】

1. Java免费视频教程

2. JAVA教程手册

3. 全面解析Java注解

以上是關於Spring Bean擴充介面的方法介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn