Spring源码分析

01、Spring源码分析:initPropertySources方法扩展点

Spring的强大之处不仅仅在于它为Java开发者提供了极大便利,更在于它的开放式架构,使得用户可以拥有最大扩展Spring的能力。

protected void initPropertySources() {
		// For subclasses: do nothing by default.
	}

在AbstractApplicationContext类中有一个initPropertySources方法是留给子类扩展,它是在refresh()的第一个方法prepareRefresh();方法中调用。

protected void prepareRefresh() {


		// Switch to active.
		// 设置容器启动的时间
		this.startupDate = System.currentTimeMillis();
		// 容器的关闭标志位
		this.closed.set(false);
		// 容器的激活标志位
		this.active.set(true);

		// 记录日志
		if (logger.isDebugEnabled()) {


			if (logger.isTraceEnabled()) {


				logger.trace("Refreshing " + this);
			}
			else {


				logger.debug("Refreshing " + getDisplayName());
			}
		}

		// Initialize any placeholder property sources in the context environment.
		// 留给子类覆盖,初始化属性资源
		initPropertySources();

		// Validate that all properties marked as required are resolvable:
		// see ConfigurablePropertyResolver#setRequiredProperties
		// 创建并获取环境对象,验证需要的属性文件是否都已经放入环境中
		getEnvironment().validateRequiredProperties();

		// Store pre-refresh ApplicationListeners...
		// 判断刷新前的应用程序监听器集合是否为空,如果为空,则将监听器添加到此集合中
		if (this.earlyApplicationListeners == null) {


			this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
		}
		else {


			// Reset local application listeners to pre-refresh state.
			// 如果不等于空,则清空集合元素对象
			this.applicationListeners.clear();
			this.applicationListeners.addAll(this.earlyApplicationListeners);
		}

		// Allow for the collection of early ApplicationEvents,
		// to be published once the multicaster is available...
		// 创建刷新前的监听事件集合
		this.earlyApplicationEvents = new LinkedHashSet<>();
	}

所以我们可以继承此类或其子类来重写initPropertySources方法,实现一些扩展。

public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
	public MyClassPathXmlApplicationContext(String... configLocations){
		super(configLocations);
	}

	@Override
	protected void initPropertySources() {
		System.out.println("扩展initPropertySource");
		//这里添加了一个name属性到Environment里面,以方便我们在后面用到
		getEnvironment().getSystemProperties().put("name","bobo");
		//这里要求Environment中必须包含username属性,如果不包含,则抛出异常
		getEnvironment().setRequiredProperties("username");
	}
}

此处我们做了两个扩展:
第一,向Environment中添加了一个属性值。
第二:我们设置了一个必要的系统属性username,当Environment中不包含username属性时系统会抛出异常。

测试类:

public class Test {

	public static void main(String[] args) {
		MyClassPathXmlApplicationContext ac = new MyClassPathXmlApplicationContext("applicationContext.xml");

//        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-${username}.xml");
	}
}

当然你也可以做其它扩展,这里只是列举了一个例子。

02、Spring源码分析:customizeBeanFactory方法扩展点

Spring的强大之处不仅仅在于它为Java开发者提供了极大便利,更在于它的开放式架构,使得用户可以拥有最大扩展Spring的能力。

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {


		// 如果属性allowBeanDefinitionOverriding不为空,设置给beanFactory对象相应属性,是否允许覆盖同名称的不同定义的对象
		if (this.allowBeanDefinitionOverriding != null) {


			beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		// 如果属性allowCircularReferences不为空,设置给beanFactory对象相应属性,是否允许bean之间存在循环依赖
		if (this.allowCircularReferences != null) {


			beanFactory.setAllowCircularReferences(this.allowCircularReferences);
		}
	}

在AbstractRefreshableApplicationContext类中有一个customizeBeanFactory方法是留给子类扩展,它是在refresh()的第二个方法obtainFreshBeanFactory()–>refreshBeanFactory()方法中调用。

protected final void refreshBeanFactory() throws BeansException {


		// 如果存在beanFactory,则销毁beanFactory
		if (hasBeanFactory()) {


			destroyBeans();
			closeBeanFactory();
		}
		try {


			// 创建DefaultListableBeanFactory对象
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			// 为了序列化指定id,可以从id反序列化到beanFactory对象
			beanFactory.setSerializationId(getId());
			// 定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖
			customizeBeanFactory(beanFactory);
			// 初始化documentReader,并进行XML文件读取及解析,默认命名空间的解析,自定义标签的解析
			loadBeanDefinitions(beanFactory);
			this.beanFactory = beanFactory;
		}
		catch (IOException ex) {


			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

此方法是用来实现BeanFactory的属性设置,主要是设置两个属性:

  • allowBeanDefinitionOverriding:是否允许覆盖同名称的不同定义的对象。
  • allowCircularReferences:是否允许bean之间的循环依赖。

如下例子:

public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {


	public MyClassPathXmlApplicationContext(String... configLocations){


		super(configLocations);
	}

	@Override
	protected void initPropertySources() {


		System.out.println("扩展initPropertySource");
		//这里添加了一个name属性到Environment里面,以方便我们在后面用到
		getEnvironment().getSystemProperties().put("name","bobo");
		//这里要求Environment中必须包含username属性,如果不包含,则抛出异常
		getEnvironment().setRequiredProperties("username");
	}

	@Override
	protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {


		super.setAllowBeanDefinitionOverriding(false);
		super.setAllowCircularReferences(false);
		super.customizeBeanFactory(beanFactory);
	}

public class Test {



	public static void main(String[] args) {


		MyClassPathXmlApplicationContext ac = new MyClassPathXmlApplicationContext("applicationContext.xml");

//        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-${username}.xml");
	}
}


}

03、Spring源码分析:自定义配置文件标签

Spring的强大之处不仅仅在于它为Java开发者提供了极大便利,更在于它的开放式架构,使得用户可以拥有最大扩展Spring的能力。

我们在用xml定义spring信息时,默认的element只包含beans,bean,import,alias这四个,其它任何标签都属于自定义标签,均需要引入相应的命名空间,如:context,aop标签等。

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {


		if (delegate.isDefaultNamespace(root)) {


			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {


				Node node = nl.item(i);
				if (node instanceof Element) {


					Element ele = (Element) node;
					if (delegate.isDefaultNamespace(ele)) {


						//处理默认的标签元素
						parseDefaultElement(ele, delegate);
					}
					else {


						//处理自定义的标签元素
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {


			delegate.parseCustomElement(root);
		}
	}

对应的源码处理在DefaultBeanDefinitionDocumentReader类的parseBeanDefinitions方法里面。

自定义标签元素

1,定义People.java

public class People {


	private String id;
	private int age;
	private String name;
	private String address;

	public People(String id, int age, String name, String address) {


		this.id = id;
		this.age = age;
		this.name = name;
		this.address = address;
	}

	public People() {


	}

	public String getId() {


		return id;
	}

	public void setId(String id) {


		this.id = id;
	}

	public int getAge() {


		return age;
	}

	public void setAge(int age) {


		this.age = age;
	}

	public String getName() {


		return name;
	}

	public void setName(String name) {


		this.name = name;
	}

	public String getAddress() {


		return address;
	}

	public void setAddress(String address) {


		this.address = address;
	}

	@Override
	public String toString() {


		return "People{" +
				"id='" + id + '\'' +
				", age=" + age +
				", name='" + name + '\'' +
				", address='" + address + '\'' +
				'}';
	}
}

2,在resources/META-INF目录下定义people.xsd,spring.handlers,spring.schemas文件

people.xsd
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
		targetNamespace="http://www.bobo.com/schema/people"
		xmlns:tns="http://www.bobo.com/schema/people"
		elementFormDefault="qualified">
	<element name="people">
		<complexType>
			<attribute name ="id" type = "string"/>
			<attribute name ="age" type = "int"/>
			<attribute name ="name" type = "string"/>
			<attribute name ="address" type = "string"/>
		</complexType>
	</element>
</schema>
spring.handlers
http\://www.bobo.com/schema/people=com.bobo.custom.PeopleNamespaceHandler
spring.schemas
http\://www.bobo.com/schema/people.xsd=META-INF/people.xsd

3,创建对应的namespaceHandler类PeopleNamespaceHandler.java

package com.wsj.custom;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

/**
 * @author wsj
 * @date 2024-04-20
 */

public class PeopleNamespaceHandler extends NamespaceHandlerSupport {


	@Override
	public void init() {


		super.registerBeanDefinitionParser("people",new PeopleBeanDefinitionParser());
	}
}

4,创建对应的BeanDefinitionParser类PeopleBeanDefinitionParser.java

package com.wsj.custom;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

/**
 * @author wsj
 * @date 2024-04-20
 */

public class PeopleBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {


	@Override
	protected Class<?> getBeanClass(Element element) {


		return People.class;
	}

	@Override
	protected void doParse(Element element, BeanDefinitionBuilder builder) {


		String id = element.getAttribute("id");
		String age = element.getAttribute("age");
		String name = element.getAttribute("name");
		String address = element.getAttribute("address");
		if (StringUtils.hasLength(id)){


			builder.addPropertyValue("id",id);
		}
		if (StringUtils.hasLength(age)){


			builder.addPropertyValue("age",age);
		}
		if (StringUtils.hasLength(name)){


			builder.addPropertyValue("name",name);
		}
		if (StringUtils.hasLength(address)){


			builder.addPropertyValue("address",address);
		}
	}
}

5,创建application-context.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:bo="http://www.bobo.com/schema/people"
	   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.xsd
			http://www.bobo.com/schema/people http://www.bobo.com/schema/people.xsd">

	<bo:people id="wsj" age="20" name="wsjxtt" address="广东省深圳市"></bo:people>

</beans>

6,创建测试类

package com.wsj;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {



	public static void main(String[] args) {


		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
		System.out.println(context.getBean("wsj"));
	}
}

运行输出

People{

	 id='wsj', age=20, name='wsjxtt', address='广东省深圳市'}
posted @ 2024-07-14 21:44  二价亚铁  阅读(32)  评论(0编辑  收藏  举报