Spring IOC-Spring扩展点大集合-第四篇

Spring是一个非常优秀的框架,其拓展点非常多,可以自定义很多功能。很多框架都采用了Spring的拓展点,例如:Dubbo,DisConf,Mybatis等。目前先梳理下Ioc部分的拓展点,文章部分内容参考:https://blog.gavinzh.com/2017/11/20/spring-develop-summary/,同时结合自己研读的源码,综合整理而来。此外,Aop部分的拓展点也很多。例如:Spring的事物管理,TransactionProxyFactoryBean、利用Aop特性+注解去做缓存拦截,CacheProxyFactoryBean、利用Aop注解对代码内的异常进行统一处理等等。后面再慢慢研究这些。

围绕生命周期展开拓展点

按照Bean的生命周期去梳理整个拓展点,这样可以很清晰的知道,在哪个阶段该使用哪个拓展点。其生命周期的范围:从Bean的定义注册开始到最终Bean实例化完成。

以下是流程图:

拓展点详解

自定义Spring XML Schema

概述

自定义Spring XML SCHEMA,这个是Spring中非常常见的拓展点,Dubbo就是用了此拓展点。例如Apache Dubbo的实现类:org.apache.dubbo.config.spring.schema.DubboNamespaceHandler。就定义了对<dubbo>标签的支持。

注意:虽然xml schema拓展点被提到了第一位,但是并不代表自定义标签对应的Bean会率先初始化。自定义标签对应的Bean依旧会是按照上图的生命周期进行执行。

org.springframework.beans.factory.xml.NamespaceHandlerSupport是一个抽象类,对org.springframework.beans.factory.xml.NamespaceHandler进行了简单封装,拓展自定义标签,可以直接实现抽象类,会方便很多。只需要实现init方法,并通过registerBeanDefinitionParser方法,传入一个标签名和org.springframework.beans.factory.xml.BeanDefinitionParser的实现类即可。如下Dubbo拓展点的代码:

public class DubboNamespaceHandler extends NamespaceHandlerSupport {

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

    @Override
    public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));
        registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
    }

}

实例

1.声明一个xsd文件,放置于文件目录resource/META-INF/下。例如:resource/META-INF/ajian.xsd

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:beans="http://www.springframework.org/schema/beans"
            xmlns:tool="http://www.springframework.org/schema/tool"
            xmlns="http://dubbo.apache.org/schema/dubbo"
            targetNamespace="http://www.ajian.com/schema/ajian">

    <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
    <xsd:import namespace="http://www.springframework.org/schema/beans" schemaLocation="http://www.springframework.org/schema/beans/spring-beans.xsd"/>
    <xsd:import namespace="http://www.springframework.org/schema/tool"/>
    <xsd:element name="ajian">
         <xsd:complexType>
            <xsd:complexContent>
                <xsd:extension base="beans:identifiedType">
                    <xsd:attribute name="name" type="xsd:string" use="required"/>
                </xsd:extension>
            </xsd:complexContent>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

2. 声明AjianNamespaceHandler类,继承NamespaceHandlerSupport。声明一个BeanDefinitionParse的实现类,即根据xml元素的内容生成一个BeanDefinition。

public class AjianNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        registerBeanDefinitionParser("ajian",new AjianBeanDefinitionParse(AjianBean.class));
    }
}
public class AjianBeanDefinitionParse implements BeanDefinitionParser {

    private Class<?> beanClass;

    public AjianBeanDefinitionParse(Class<?> beanClass) {
        this.beanClass = beanClass;
    }

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
        rootBeanDefinition.setBeanClass(beanClass);
        rootBeanDefinition.setLazyInit(false);
        // 设置属性
        String name = element.getAttribute("name");
        rootBeanDefinition.getPropertyValues().addPropertyValue("name",name);
        parserContext.getRegistry().registerBeanDefinition(name,rootBeanDefinition);
        return rootBeanDefinition;
    }
}

3.在resource/META-INF下声明两个文件,spring.handlers与spring.schemas。

spring.handlers内容

http\://www.ajian.com/schema/ajian=com.yangt.risk.ao.ioc.AjianNamespaceHandler

spring.schemas内容

http\://www.ajian.com/schema/ajian/ajian.xsd=META-INF/ajian.xsd

4.修改spring-ioc.xml加上新标签的声明,如下

5.测试代码如下:

自定义Bean被解析部分的源码

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
		String namespaceUri = getNamespaceURI(ele);
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler == null) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		}
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}

通过元素找到nameSpaceUri,然后通过nameSpaceUri找到对应的NamespaceHandler,最后调用对应的parse方法解析为具体的BeanDefinition。所以需要实现NamespaceHandler接口和BeanDefinitionParser接口。

BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProcessor,对外提供一个拓展方法:

void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

此方法,允许修改已经注册到Registry内的BeanDefinition,或者添加一些自定义BeanDefinition到Registry,同时也支持删除某些BeanDefinition。因为此时这些bean还都只是BeanDefinition,未被实例化。执行时机会在普通BeanFactoryPostProcessor接口实现类执行之前被调用。

Spring源码内调用时机:

AbstractApplicationCotext refresh方法:

String[] postProcessorNames =
					beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
// 判断是否实现了PriorityOrdered接口
			for (String ppName : postProcessorNames) {
				if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					processedBeans.add(ppName);
				}
			}
// 排序
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			registryProcessors.addAll(currentRegistryProcessors);
// 循环调用所有实现了BeanDefinitionRegistryPostProcessor的子类
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
			currentRegistryProcessors.clear();

			// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
			postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
// 判断是否实现了Ordered接口
			for (String ppName : postProcessorNames) {
				if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					processedBeans.add(ppName);
				}
			}
// 排序
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			registryProcessors.addAll(currentRegistryProcessors);
// 循环调用所有实现了BeanDefinitionRegistryPostProcessor的子类
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
			currentRegistryProcessors.clear();

			// 最后调用其他实现了BeanDefinitionRegistryPostProcessors接口的子类
			boolean reiterate = true;
			while (reiterate) {
				reiterate = false;
				postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
				for (String ppName : postProcessorNames) {
					if (!processedBeans.contains(ppName)) {
						currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
						processedBeans.add(ppName);
						reiterate = true;
					}
				}
// 排序
				sortPostProcessors(currentRegistryProcessors, beanFactory);
				registryProcessors.addAll(currentRegistryProcessors);
// 循环调用所有实现了BeanDefinitionRegistryPostProcessor的子类
				invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
				currentRegistryProcessors.clear();
			}

			// 调用所有实现类BeanFactoryPostProcessors接口的子类
			invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
			invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);

执行顺序:

  • 首先,实现了BeanDefinitionRegistryPostProcessor和PriorityOrdered的接口的类先执行。
  • 其次,执行实现了BeanDefinitionRegistryPostProcessor和Ordered的接口的类。
  • 最后,调用实现了BeanDefinitionRegistryPostProcessor接口的类。

实例:

disconf-client内DisconfMgrBean

public class DisconfMgrBean implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ApplicationContextAware {
    
    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 1;
    }


    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

        // 留个口子可以方便的替换配置文件
        if (StringUtils.isNotBlank(disconfFileName)) {
            SupportFileTypeEnum fileTypeEnum = SupportFileTypeEnum.getByFileName(disconfFileName);
            if (fileTypeEnum != SupportFileTypeEnum.PROPERTIES) {
                throw new RuntimeException("disconfFileName 配置错误,必须为properties 文件");
            }
            DisClientConfig.setDisconfFileName(disconfFileName);
        }
        List<String> scanPackList = Lists.newArrayList();
        if (StringUtils.isNotBlank(scanPackage)) {
            // unique
            Set<String> hs = Sets.newHashSet(StringUtil.parseStringToStringList(scanPackage, SCAN_SPLIT_TOKEN));
            scanPackList = Lists.newArrayList(hs);
        }

        // 进行扫描
        DisconfMgr.getInstance().setApplicationContext(applicationContext);
        DisconfMgr.getInstance().firstScan(scanPackList);

        // register java bean
        registerAspect(registry);
    }



}

此类执行的顺序是目前的项目中是第一位的,主要是实现了BeanDefinitionRegistryPostProcessor, PriorityOrdered。同时getOrder的顺序是最高的,所以postProcessBeanDefinitionRegistry方法会先被回调。

BeanFactoryPostProcessor

允许修改程序上下文中的BeanDefinition,可以对BeanFactory这种Bean的属性值进行修改,一个典型的应用是PropertyResourceConfigurer。

实例

@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		try {
			Properties mergedProps = mergeProperties();

			// Convert the merged properties, if necessary.
			convertProperties(mergedProps);

			// Let the subclass process the properties.
			processProperties(beanFactory, mergedProps);
		}
		catch (IOException ex) {
			throw new BeanInitializationException("Could not load properties", ex);
		}
	}

真正的替换属性的逻辑

/**
	 * Apply the given property value to the corresponding bean.
	 */
	protected void applyPropertyValue(
			ConfigurableListableBeanFactory factory, String beanName, String property, String value) {

		BeanDefinition bd = factory.getBeanDefinition(beanName);
		while (bd.getOriginatingBeanDefinition() != null) {
			bd = bd.getOriginatingBeanDefinition();
		}
		PropertyValue pv = new PropertyValue(property, value);
		pv.setOptional(this.ignoreInvalidKeys);
		bd.getPropertyValues().addPropertyValue(pv);
	}

比如说:可能某些参数是${key},这个实现类就是把前边这种参数转换成xxx.properties中key所对应的值。

Spring源码内调用时机:

同理,也是在AbstractApplicationContext的refresh方法内的invokeBeanFactoryPostProcessors(beanFactory);内被调用,但是执行时机在BeanDefinitionRegistryPostProceesor之后。

InstantiationAwareBeanPostProcessor

这个接口是BeanPostProcessor的子接口,用于在实例化之后,但在设置显式属性或自动装配之前,设置实例化之前的回调函数。通常用于抑制特定目标bean的默认实例化,例如,创建具有特殊TargetSources(池化目标,延迟初始化目标等)的代理,或者实现其他注入策略,例如字段注入。注意:这个接口是一个专用接口,主要用于框架内的内部使用。 建议尽可能实现简单的BeanPostProcessor接口,或者从InstantiationAwareBeanPostProcessorAdapter派生,以便屏蔽此接口的扩展。

postProcessBeforeInstantiation方法,在目标bean实例化之前创建bean,如果在这里创建了bean,则不会走默认的实例化过程,通常用来创建代理。注意工厂方法生成的bean不会走这个方法。

postProcessAfterInstantiation方法,在目标bean实例化后,但是没有进行属性填充前执行的方法。

postProcessPropertyValues方法,在将给定属性值设置到到给定的bean后,对其进行后处理。 允许检查所有的依赖关系是否被满足,例如基于bean属性设置器上的“Required”注解。还允许替换要应用的属性值,通常通过创建基于原始PropertyValues的新MutablePropertyValues实例,添加或删除特定值。

BeanPostProcessor

对bean进行处理的回调接口,有两个方法:

postProcessBeforeInitialization:在bean初始化(如InitializingBean的afterPropertiesSet或自定义init方法)之前进行回调。

postProcessAfterInitialization:在bean初始化之后(如InitializingBean的afterPropertiesSet或自定义init方法)进行回调。

实例: ApplicationContextAwareProcessor

@Override
	public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
		AccessControlContext acc = null;

		if (System.getSecurityManager() != null &&
				(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
						bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
						bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
			acc = this.applicationContext.getBeanFactory().getAccessControlContext();
		}

		if (acc != null) {
			AccessController.doPrivileged(new PrivilegedAction<Object>() {
				@Override
				public Object run() {
					invokeAwareInterfaces(bean);
					return null;
				}
			}, acc);
		}
		else {
// 对所有实现aware接口的类,set注入ApplicationContext上下文
			invokeAwareInterfaces(bean);
		}

		return bean;
	}
// 注入上下文操作
private void invokeAwareInterfaces(Object bean) {
		if (bean instanceof Aware) {
			if (bean instanceof EnvironmentAware) {
				((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
			}
			if (bean instanceof EmbeddedValueResolverAware) {
				((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
			}
			if (bean instanceof ResourceLoaderAware) {
				((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
			}
			if (bean instanceof ApplicationEventPublisherAware) {
				((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
			}
			if (bean instanceof MessageSourceAware) {
				((MessageSourceAware) bean).setMessageSource(this.applicationContext);
			}
			if (bean instanceof ApplicationContextAware) {
				((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
			}
		}
	}

InitializingBean

如果bean实现了InitializingBean接口,初始化时,则会调用afterPropertiesSet方法。

Bean显示声明init-method

执行完InitializingBean的afterPropertiesSet方法后,则会调用在bean上显示声明的init-method。

结论

以上是大部分拓展点执行的时机以及部分实例,还有部分源码分析。总结来看,最常用的几个拓展点:

  • BeanDefinitionRegistryPostProcessor
  • BeanFactoryPostProcessor
  • BeanPostProcssor
  • InitializingBean
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值