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='广东省深圳市'}
  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring 分析是一个相对复杂和庞大的话题,无法在一次对话中详细讨论。不过,我可以给你提供一些关于 Spring 分析的一般指导和建议。 1. 了解 Spring 的核心模块:Spring 框架是模块化的,每个模块都有不同的功能和职责。在开始分析之前,你需要先了解 Spring 的核心模块,如 Spring Core、Spring MVC、Spring Data 等。 2. 阅读官方文档和码注释:Spring 框架的官方文档和码注释是你学习和理解码的重要资。官方文档提供了对 Spring 各个模块的详细说明,码注释则解释了代码的作用和实现细节。 3. 调试和跟踪代码:在进行分析时,调试器是你的好帮手。通过设置断点、单步跟踪以及观察变量的值,你可以深入理解代码的执行流程和逻辑。 4. 理解设计模式和原理:Spring 框架采用了许多设计模式和原理来实现其功能。在分析码时,你需要熟悉这些设计模式和原理,例如依赖注入、AOP、工厂模式等。 5. 参考开社区和博客:Spring 框架是一个非常活跃的开社区,许多开发者在博客和论坛上分享了他们的分析和理解。阅读这些文章可以帮助你更好地理解 Spring 框架的实现细节。 请记住,深入分析 Spring 码需要耐心和时间投入,同时也需要有一定的 Java 和设计模式的基础。希望这些指导对你有所帮助!如果你有具体的问题或者需要更详细的信息,欢迎继续提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二价亚铁.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值