如何玩转Spring的BeanPostProcessor.

    在Spring初始化bean的步骤中,有一步执行前缀/后置初始化的处理(BeanPostProcessor)的逻辑。这为我们提供了扩展Bean的功能,IOC的强大功能就在于此。

    在讲解之前,这里要说一下InitializingBean和BeanPostProcessor的区别,必免混淆。两个接口都可以执行bean初始化前置处理的功能。区别在于InitializingBean是目标Bean主动实现的接口(常用业务逻辑可以写在里面),而BeanPostProcessor是由BeanFatotry在Bean初始化时把Bean作为参数传入BeanPostProcessor执行的(具体BeanPostProcessor是被存储在AbstractBeanFactory中的一个List中)。暂且说成被动执行。

    BeanPostProcessor的实现类主要是Spring自带的一些辅助类,比如注解扫描类(CommonAnnotationBeanPostProcessor,xml中配置为<context:component-scan> ),配置以后会自动扫描标记了@Controller,@Service等注解的类,提供给其它类进行注入。

    下面介绍另一个配合hibernate使用BeanPostProcessor的另一个场景。我们有这样一种场景,如何让容器在启动的时候加载各模块之间配置的mapping文件呢。

     首先看一下常规的做法,在sessionFactory中显示的配置。
	<bean id="routeSessionFactory" name="routeSessionFactory"
		class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="dataSource" ref="routeDataSourceLocal" />
		<property name="mappingResources">
			<list>
				<value>com/sf/module/routeshare/META-INF/config/mapping.xml</value>
.....//添加其它mapping.xml文件
			</list>
		</property>

    此种做法不好的地方在于多个模块同时开发时,需要修改同一个文件,不利于管理。

    其实BeanPostProcessor提供了一种便捷的方式,在容器启动时,先收集好mapping.xml文件的位置,在实例化LocalSessionFactoryBean时把mapping数组注入到mappingResources属性中。
    具体步骤如下:
    1.web.xml中配置容器启动后的监听器。
  <listener>
    <listener-class>com.xxx.framework.server.core.deploy.FrameworkListener</listener-class>
  </listener>


    2.加载所有mapping文件的路径。
    //始始化容器时读取mapping文件路径。
    private Application initApplication()
    {
       //加载指定路径下各模块的mapping.xml文件。
       List mappingConfigs = initConfigs("/META-INF/config/mapping.xml", 
moduleNames);
...}

    //使用spring工具类进行解析
    private List initConfigs(String configName, List configs)
    {
        try
        {   
            Resource resources[] = new PathMatchingResourcePatternResolver().getResources((new StringBuilder("classpath*:com/xxx/**")).append(configName).toString());
            for(int i = 0; i < resources.length; i++)
            {
                Resource resource = resources[i];
                try
                {
                    String file = resource.getURL().getPath();
                    String config = file.substring(file.lastIndexOf("com/sf/"));
                    if(!configs.contains(config))
                    {
                        configs.add(config);
                    }
                    .....
					
	}


       3.编写BeanPostProcessor。
public class MappingAutowiring
    implements BeanPostProcessor
{

    public MappingAutowiring()
    {
    }

    public String[] getMappingResources()
    {
        return mappingResources;
    }
    
    //此方法通过MappingFactoryBean进行注入,里面会获取到之前Application的mapping的路径
    public void setMappingResources(String mappingResources[])
    {
        this.mappingResources = mappingResources;
    }

    public Object postProcessBeforeInitialization(Object bean, String beanName)
        throws BeansException
    {
        if(bean instanceof LocalSessionFactoryBean)
        {
            log.debug((new StringBuilder("============ MappingAutowiring: ")).append(mappingResources).toString());
            ((LocalSessionFactoryBean)bean).setMappingResources(mappingResources);
        }
        return bean;
    }

<!--配置MappingAutowiring -->
<bean id="mappingAutowiring" class="com.xxx.framework.server.core.deploy.MappingAutowiring">
		<property name="mappingResources" ref="mappingResources" />
	</bean>
	<bean id="mappingResources" class="com.xxx.framework.server.core.deploy.MappingFactoryBean" />


public class MappingFactoryBean
    implements FactoryBean
{
    public MappingFactoryBean(){}

    public Object getObject()
        throws Exception
    {
        List configs = ApplicationContext.getContext().getApplication().getMappingConfigs();
       
        return ((Object) (configs.toArray(new String[configs.size()])));
    }
	
    ....
}

    从上述代码看出,通过BeanPostProcessor的postProcessBeforeInitialization方法,可以给LocalSessionFactoryBean注入mapping文件的路径

猜你喜欢

转载自beck5859509.iteye.com/blog/2170911