01、Spring源码分析:initPropertySources方法扩展点
Spring的强大之处不仅仅在于它为Java开发者提供了极大便利,更在于它的开放式架构,使得用户可以拥有最大扩展Spring的能力。
| protected void initPropertySources() { |
| |
| } |
在AbstractApplicationContext类中有一个initPropertySources方法是留给子类扩展,它是在refresh()的第一个方法prepareRefresh();方法中调用。
| protected void prepareRefresh() { |
| |
| |
| |
| |
| 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()); |
| } |
| } |
| |
| |
| |
| initPropertySources(); |
| |
| |
| |
| |
| getEnvironment().validateRequiredProperties(); |
| |
| |
| |
| if (this.earlyApplicationListeners == null) { |
| |
| |
| this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners); |
| } |
| else { |
| |
| |
| |
| |
| this.applicationListeners.clear(); |
| this.applicationListeners.addAll(this.earlyApplicationListeners); |
| } |
| |
| |
| |
| |
| this.earlyApplicationEvents = new LinkedHashSet<>(); |
| } |
所以我们可以继承此类或其子类来重写initPropertySources方法,实现一些扩展。
| public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext { |
| public MyClassPathXmlApplicationContext(String... configLocations){ |
| super(configLocations); |
| } |
| |
| @Override |
| protected void initPropertySources() { |
| System.out.println("扩展initPropertySource"); |
| |
| getEnvironment().getSystemProperties().put("name","bobo"); |
| |
| getEnvironment().setRequiredProperties("username"); |
| } |
| } |
此处我们做了两个扩展:
第一,向Environment中添加了一个属性值。
第二:我们设置了一个必要的系统属性username,当Environment中不包含username属性时系统会抛出异常。
测试类:
| public class Test { |
| |
| public static void main(String[] args) { |
| MyClassPathXmlApplicationContext ac = new MyClassPathXmlApplicationContext("applicationContext.xml"); |
| |
| |
| } |
| } |
当然你也可以做其它扩展,这里只是列举了一个例子。
02、Spring源码分析:customizeBeanFactory方法扩展点
Spring的强大之处不仅仅在于它为Java开发者提供了极大便利,更在于它的开放式架构,使得用户可以拥有最大扩展Spring的能力。
| protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { |
| |
| |
| |
| if (this.allowBeanDefinitionOverriding != null) { |
| |
| |
| beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); |
| } |
| |
| if (this.allowCircularReferences != null) { |
| |
| |
| beanFactory.setAllowCircularReferences(this.allowCircularReferences); |
| } |
| } |
在AbstractRefreshableApplicationContext类中有一个customizeBeanFactory方法是留给子类扩展,它是在refresh()的第二个方法obtainFreshBeanFactory()–>refreshBeanFactory()方法中调用。
| protected final void refreshBeanFactory() throws BeansException { |
| |
| |
| |
| if (hasBeanFactory()) { |
| |
| |
| destroyBeans(); |
| closeBeanFactory(); |
| } |
| try { |
| |
| |
| |
| DefaultListableBeanFactory beanFactory = createBeanFactory(); |
| |
| beanFactory.setSerializationId(getId()); |
| |
| customizeBeanFactory(beanFactory); |
| |
| 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"); |
| |
| getEnvironment().getSystemProperties().put("name","bobo"); |
| |
| 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"); |
| |
| |
| } |
| } |
| |
| |
| } |
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 + '\'' + |
| '}'; |
| } |
| } |
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; |
| |
| |
| |
| |
| |
| |
| 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; |
| |
| |
| |
| |
| |
| |
| 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='广东省深圳市'} |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2023-07-14 Python保姆级教程 数据类型—新手小白入门必看系列