Spring(七):修饰Bean(自定义标签与属性的解析)

回顾

前面两节已经看完了对一个Bean标签的解析了,至此一个完整的GenericBeanDefinition就诞生了,完成了从XML文档到GenericBeanDefinition的转换,现在XML的所有配置,我们都可以从GenericBeanDefinition中找到对应的配置

但其实,GenericBeanDefinition只是一个子类而已,大多数的实现细节还是在AbstractBeanDefinition里面,就好像前面看到的,对于标签对应的实体类存储,存储容器都是在AbstractBeanDefinition里面的

下面就来看看AbstractBeanDefinition的功能吧

AbstractBeanDefinition

BeanDefinition重点在于其成员变量,大多数的方法都是对成员变量进行操作的,下面就来看下AbstractBeanDefinition的成员变量

	//bean的作用范围,对应属性为bean标签的scope
	public static final String SCOPE_DEFAULT = "";

	//bean是否自动注入
	public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;

	//bean自动注入的方式为byName
	public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;

	//bean自动注入的方式为byType
	public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;

	//bean通过构造器进行自动注入
	public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;

	/**
	 * Constant that indicates determining an appropriate autowire strategy
	 * through introspection of the bean class.
	 * @see #setAutowireMode
	 * @deprecated as of Spring 3.0: If you are using mixed autowiring strategies,
	 * use annotation-based autowiring for clearer demarcation of autowiring needs.
	 */
	@Deprecated
	public static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;

	/**
	 * Constant that indicates no dependency check at all.
	 * @see #setDependencyCheck
	 */
	public static final int DEPENDENCY_CHECK_NONE = 0;

	/**
	 * Constant that indicates dependency checking for object references.
	 * @see #setDependencyCheck
	 */
	public static final int DEPENDENCY_CHECK_OBJECTS = 1;

	/**
	 * Constant that indicates dependency checking for "simple" properties.
	 * @see #setDependencyCheck
	 * @see org.springframework.beans.BeanUtils#isSimpleProperty
	 */
	public static final int DEPENDENCY_CHECK_SIMPLE = 2;

	/**
	 * Constant that indicates dependency checking for all properties
	 * (object references as well as "simple" properties).
	 * @see #setDependencyCheck
	 */
	public static final int DEPENDENCY_CHECK_ALL = 3;

	/**
	 * Constant that indicates the container should attempt to infer the
	 * {@link #setDestroyMethodName destroy method name} for a bean as opposed to
	 * explicit specification of a method name. The value {@value} is specifically
	 * designed to include characters otherwise illegal in a method name, ensuring
	 * no possibility of collisions with legitimately named methods having the same
	 * name.
	 * <p>Currently, the method names detected during destroy method inference
	 * are "close" and "shutdown", if present on the specific bean class.
	 */
	public static final String INFER_METHOD = "(inferred)";


	@Nullable
	private volatile Object beanClass;
	//bean的作用范围
	@Nullable
	private String scope = SCOPE_DEFAULT;
	//bean是不是一个抽象bean,对应属性abstract
	private boolean abstractFlag = false;
	//bean是不是延迟加载,对应属性lazy-init
	@Nullable
	private Boolean lazyInit;
	//bean的自动注入模式,对应autowire
	private int autowireMode = AUTOWIRE_NO;
	//依赖进行检查(3.0放弃了该功能)
	private int dependencyCheck = DEPENDENCY_CHECK_NONE;
	//实例化该bean是否需要先实例化其他一些bean,对应bean的depend-on属性
	@Nullable
	private String[] dependsOn;
	//该bean是否考虑被当为候选bean,只有被当为候选bean,才能被注入
	private boolean autowireCandidate = true;
	//当出现多个bean当候选者时,该属性为true会被优先考虑当候选者
	private boolean primary = false;
	//存取Qualifier标签对象
	private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>();
	//
	@Nullable
	private Supplier<?> instanceSupplier;
	//允许访问非公开的构造器和方法,后面反射需要用到
	private boolean nonPublicAccessAllowed = true;
	//是否以宽松模式去解析构造函数
	private boolean lenientConstructorResolution = true;
	//bean工厂的名字
	@Nullable
	private String factoryBeanName;
	//bean工厂的方法名字
	@Nullable
	private String factoryMethodName;
	//记录construct-arg标签对象的,即构造函数给的参数
	@Nullable
	private ConstructorArgumentValues constructorArgumentValues;
	//记录property标签的
	@Nullable
	private MutablePropertyValues propertyValues;
	//记录lookup-method、replaced-method标签的
	private MethodOverrides methodOverrides = new MethodOverrides();
	//初始化bean的方法,对应bean的init-method,即创建bean时就会调用
	@Nullable
	private String initMethodName;
	//销毁Bean的方法,即bean的生命周期过了之后会调用的方法
	@Nullable
	private String destroyMethodName;
	//是否执行init-method
	private boolean enforceInitMethod = true;
	//是否执行destiry-method
	private boolean enforceDestroyMethod = true;
	//标明该bean是用户定义的,还是程序本身定义的
	private boolean synthetic = false;
	//
	private int role = BeanDefinition.ROLE_APPLICATION;
	//bean的描述信息
	@Nullable
	private String description;
	//bean的资源来源,也就是XML文件
	@Nullable
	private Resource resource;

下一步:对创建的BeanDefinition进行修饰

现在我们已经解析完XML,并且产生了BeanDefinition对象了,但这一大串的逻辑仅仅只是转换成功而已,还没有进行注册了,现在返回BeanDefinitionDocumentReader的processBeanDefinition里面
在这里插入图片描述
我们仅仅只是完成了bgHolder的创建,但还没有进行下面的修饰和注册了

从代码上可以看到,XML转换成BeanDefinition是由delegate(BeanDefinitionParserDelegate)去实现,然后对新建的BeanDefinition进行修饰,同样也是交给了delegate去做,而注册BeanDefinition的逻辑是交由BeanDefinitionReaderUtils去实现

为什么要进行修饰呢?下面来看一个场景

<bean id="test" class="test.Myclass">
	<mybean:user username="abc"/>
</bean>

可以看到,这一段Bean的XML配置使用了自定义类型的解析

拓展:对于bean的解析分为两种类型

  • 一种是默认类型的解析
  • 另一种是自定义类型的解析

而上面的正是为自定义类型解析,前面已经基本上对bean标签做了默认的解析,但对于自定义类型的解析,要在哪里做呢?

这一切的逻辑都在下一行的代码执行的方法,decorateBeanDefinitionIfRequired

源码如下
在这里插入图片描述

注意这里,有一个参数为null了!

第三个参数其实是父类的bean,当对某个嵌套配置进行分析时,这里是需要传递父类的BeanDefinition的

	public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
			Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
		// 记录解析了默认标签的bean
		BeanDefinitionHolder finalDefinition = originalDef;
		// Decorate based on custom attributes first.
        // 获取bean标签的所有属性
		NamedNodeMap attributes = ele.getAttributes();
        //遍历所有属性
		for (int i = 0; i < attributes.getLength(); i++) {
            //获取当前属性
			Node node = attributes.item(i);
            //对该属性进行修饰
			finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
		}
		// 
		// Decorate based on custom nested elements.
        //获取bean标签的所有子标签
		NodeList children = ele.getChildNodes();
        //遍历所有子标签
		for (int i = 0; i < children.getLength(); i++) {
			Node node = children.item(i);
            //如果当前的子标签
            //并且如果子标签是一个XML标签的子标签
            //就进行修饰
			if (node.getNodeType() == Node.ELEMENT_NODE) {
				finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
			}
		}
		return finalDefinition;
	}

可以看到,这个方法对标签进行了修饰,分别修饰两个方面,并且修饰的方法是一样的!!!

  • 属性
  • 子标签

因为修饰方法是一样的,所以就不细分了

修饰子标签与属性

	public BeanDefinitionHolder decorateIfRequired(
			Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
		
        //获取标签的命名空间
		String namespaceUri = getNamespaceURI(node);
        //如果命名空间不为空,并且不是默认的命名空间,才进行下面处理
		if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
            //根据命名空间找到对应的处理器
            //命名空间决定了哪些标签能用
            //因为可以通过命名空间找到那些标签的处理器
            //有处理器才能使用标签
			NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
            //判断是否找到处理器
			if (handler != null) {
                //用处理器进行修饰
				BeanDefinitionHolder decorated =
						handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
                //修饰的结果不为空,返回结果
				if (decorated != null) {
					return decorated;
				}
			}
            //如果找不到处理器且命名空间是spring的命名空间
            //抛出错误,自定义的标签怎么可能使用sprinf的命名空间!!!
			else if (namespaceUri.startsWith("http://www.springframework.org/schema/")) {
				error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
			}
            //其他情况,即没有找到处理器,且又不是Spring的命名空间
            //抛出错误,没有处理器可以解析XML标签
			else {
				// A custom namespace, not to be handled by Spring - maybe "xml:...".
				if (logger.isDebugEnabled()) {
					logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
				}
			}
		}
        //如果没有命名空间或者是默认的命名空间,不处理直接返回
		return originalDef;
	}

可以对于子标签和属性之所以可以一起处理,是因为对于子标签和属性,命名空间都有对应的处理器去处理,这里我们暂且将进来二点对象默认为子结点(属性与子标签),步骤如下

  • 获取子节点的命名空间,如果没有命名空间或者命名空间为默认的命名空间,什么都不做
  • 根据命名空间去获取处理器,利用处理器去处理子结点
    • 如果获取不到处理器且命名空间为spring的命名空间,抛错,spring的命名空间不支持自定义标签解析!!!
    • 如果获取不到处理器且命名空间也不为spring的,抛错,没有对应的命名空间来解析XML

总结一下

  • decorateBeanDefinitionIfRequired是对自定义的子标签和属性进行处理的(对于程序默认的标签处理其实是直接略过的),底层修饰的细节为decorateIfRequired方法
  • decorateIfRequired这个方法通过子标签的命名空间,去寻找处理器进行下一步解析
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值