dubbo-config-spring自定義xml標籤擴充套件
要實現自定義自定義標籤擴充套件,需要有如下步驟(在spring中定義了兩個介面NamespaceHandler、BeanDefinitionParser,用來實現擴充套件)
1.設計配置屬性和JavaBean,編寫XSD檔案;
2.NamespaceHandler註冊一堆BeanDefinitionParser,利用它們來進行解析;
3.BeanDefinitionParser用於解析每個element的內容;
4.編寫Spring.handlers和Spring.schemas檔案以供Spring讀取;Spring預設會載入jar包下的META-INF/spring.handlers檔案尋找對應的NamespaceHandler;
Dubbo中Spring擴充套件就是使用Spring的自定義型別,所以同樣也有NamespaceHandler、BeanDefinitionParser;
org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
org.apache.dubbo.config.spring.schema.DubboBeanDefinitionParser
org.apache.dubbo.config.spring.schema.DubboNamespaceHandler#init,該方法用於註冊BeanDefinitionParser
@Override public void init() { registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true)); registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true)); registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true)); registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true)); registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true)); registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true)); registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.class, true)); registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true)); registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true)); registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true)); registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser()); }
對應的Bean
在Spring啟動解析相應的配置標籤時,相應的啟動provider釋出服務註冊服務,而同時讓consumer在啟動的時候自動訂閱發現服務,加入了兩個Bean, ServiceBean、ReferenceBean,分別繼承ServiceConfig和ReferenceConfig;同時還分別實現了InitializingBean、DisposableBean, ApplicationContextAware, ApplicationListener, BeanNameAware介面;
InitializingBean:為Bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是實現該介面的類,在初始化Bean的時候會執行該方法;
DisposableBean :Bean被銷燬的時候,spring容器會自動執行destory方法,比如釋放資源
ApplicationContextAware: 實現了這個介面的Bean,當Spring容器初始化的時候,會自動的將ApplicationContext注入進來;
ApplicationListener :ApplicationEvent事件監聽,Spring容器啟動後會發一個事件通知;
BeanNameAware :獲得自身初始化時,本身的Bean的id屬性;
下面根據Spring提供介面仿寫一個自定義xml標籤擴充套件
BeanDefinitionParser 用於標籤解析
/** * 用於標籤解析 */ public class BeanDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override protected Class<?> getBeanClass(Element element) { return CommonBean.class; } @Override protected void doParse(Element element, BeanDefinitionBuilder builder) { String id = element.getAttribute("id"); String beanName = element.getAttribute("beanName"); String createTime = element.getAttribute("createTime"); if (StringUtils.hasText(id)) { builder.addPropertyValue("id", id); } if (StringUtils.hasText(beanName)) { builder.addPropertyValue("beanName", beanName); } if (StringUtils.hasText(createTime)) { builder.addPropertyValue("createTime", createTime); } } }
BeanNamespaceHandler呼叫標籤解析處理
/** * 呼叫標籤解析處理 */ public class BeanNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { // 將節點名與解析類對映,當節點名稱為bean,使用BeanDefinitionParser進行解析 registerBeanDefinitionParser("bean", new BeanDefinitionParser()); } }
自定義標籤配置,需要在META-INF下建立兩個預設Spring配置檔案來提供支援,一個是spring.schemas,另一個是spring.handlers,前者是為了驗證自定義的xml配置檔案是否符合要求,後者是定義Spring解析的配置檔案;
spring.handlers
http\://org.example/schemas/bean=com.example.bean.schema.BeanNamespaceHandler
spring.schemas
http\://org.example/schemas/bean.xsd=META-INF/bean.xsd
定義一個與自定義配置標籤相對應的JavaBean,可根據需要是否實現InitializingBean,ApplicationContextAware等介面
public class CommonBean implements InitializingBean, ApplicationContextAware { protected String id; protected String beanName; protected String createTime; private transient ApplicationContext applicationContext; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getBeanName() { return beanName; } public void setBeanName(String beanName) { this.beanName = beanName; } public String getCreateTime() { return createTime; } public void setCreateTime(String createTime) { this.createTime = createTime; } @Override public void afterPropertiesSet() { System.out.println(applicationContext.getBeansOfType(this.getClass())); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public String toString() { return "CommonBean{" + "id='" + id + '\'' + ", beanName='" + beanName + '\'' + ", creteTime='" + createTime + '\'' + '}'; } }
建立一個工程進行測試
在resources目錄下建立bean.xml
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:customer="http://org.example/schemas/bean" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://org.example/schemas/bean http://org.example/schemas/bean.xsd"> <customer:bean id="user" beanName="test1" createTime="2020-08-12"/> </beans>
測試類
public class MyTest { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); context.start(); } }