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='廣東省深圳市'}

浙公網安備 33010602011771號