从零RPC(六) 诸多框架整和Spring的秘密

本章内容:

1、Spring FactoryBean、InitializingBean的作用。

2、自定义xsd文件。用来自定义Spring xml文件的标签。

3、扩展BeanDefinitionParser建立自定义标签解析成为我们自定义bean的过程。

4、自定义bean的后置操作完成Netty服务开启(后面会介绍)、注册中心注册等动作。

 

一、Spring FactoryBean、InitializingBean的作用。

Spring的生命周期后面单独写Spring时介绍。现在只介绍 FactoryBean、InitializingBean这两个接口。

1、InitializingBean。 当我们把一个类实例化的过程交给IOC容器托管时,希望在该类实例化后做一些操作。就可以实现这个接口。

并在afterPropertiesSet()方法中写入逻辑。

2、FactoryBean。当我们自定义一个类,在这个类中得到另一个对象、类型。可以用这个接口。这样说有点抽象。比如一个具体场景:装饰者类(装饰者模式)。 我们希望在真正执行逻辑的类外面有个包装类,来做一些事情。就可以用这个接口,把装饰类(当前类本身)与被装饰的类(getObject()得到的对象)之间绑定。从而做一些逻辑。

在Rpc里利用这两个特性,后置动作中放入Netty服务的开启、注册中心的注册。装饰类来装饰对应的每个接口的实现类。

/**
 * 
 * 

Title: ProviderFactoryBean.java

*

Description:

* @author zhaojunjie * @date 2020年3月28日 * @version 1.0 */ public class ProviderFactoryBean implements InitializingBean,FactoryBean{ private Class serviceInterface; private Object serviceObject; private String servicePort; private long timeout; private Object serviceProxyObject; private String appKey; private String groupName; private int weight = 1; private int workerThread = 10; @Override public Object getObject() throws Exception { return serviceObject; } @Override public Class getObjectType() { return serviceInterface; } @Override public void afterPropertiesSet() throws Exception { //1、开启NettyServer System.out.println(this.toString()); //2、注册到zookeeper注册中心 List serviceMetaData = buildProviderServiceInfo(); RegisterCenter.singleton().registerProvider(serviceMetaData); } private List buildProviderServiceInfo() { List providerList = Lists.newArrayList(); Method[] methods = serviceObject.getClass().getDeclaredMethods(); for (Method method : methods) { ProviderService providerService = new ProviderService(); providerService.setServiceInterface(serviceInterface); providerService.setServiceObject(serviceObject); providerService.setServerIp(IPHelper.getCurrentIp()); providerService.setServicePort(Integer.parseInt(servicePort)); providerService.setTimeout(timeout); providerService.setServiceMethod(method); providerService.setWeight(weight); providerService.setWorkerThread(workerThread); providerService.setAppKey(appKey); providerService.setGroupName(groupName); providerList.add(providerService); } return providerList; } public Class getServiceInterface() { return serviceInterface; } public void setServiceInterface(Class serviceInterface) { this.serviceInterface = serviceInterface; } public Object getServiceObject() { return serviceObject; } public void setServiceObject(Object serviceObject) { this.serviceObject = serviceObject; } public String getServicePort() { return servicePort; } public void setServicePort(String servicePort) { this.servicePort = servicePort; } public long getTimeout() { return timeout; } public void setTimeout(long timeout) { this.timeout = timeout; } public Object getServiceProxyObject() { return serviceProxyObject; } public void setServiceProxyObject(Object serviceProxyObject) { this.serviceProxyObject = serviceProxyObject; } public String getAppKey() { return appKey; } public void setAppKey(String appKey) { this.appKey = appKey; } public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } public int getWorkerThread() { return workerThread; } public void setWorkerThread(int workerThread) { this.workerThread = workerThread; } @Override public String toString() { return "ProviderFactoryBean [serviceInterface=" + serviceInterface + ", serviceObject=" + serviceObject + ", servicePort=" + servicePort + ", timeout=" + timeout + ", serviceProxyObject=" + serviceProxyObject + ", appKey=" + appKey + ", groupName=" + groupName + ", weight=" + weight + ", workerThread=" + workerThread + "]"; } }

二、自定义xsd文件。用来自定义Spring xml文件的标签。

1、xsd文件的定义。文件放在resources/META-INF/ 目录下.比如 resources/META-INF/back-service.xsd

xmlns中定义地址:http://www.back.com/schema/back-service

定义Element的name 这个一定注意。解析时会根据这个name,找对应的解析类。再下面就是属性名称和类型的定义。相信一目了然,不再介绍。


	
	

	
	
	
		
			
			
			
			
			
			
			
			
		
	
	
	

2、编写完成xsd后新建Spring的xml 我们来看下我们自定义标签的效果。xml上方要引入我们自定义的地址与xsd.同时把地址与本地xsd文件绑定。eclipse中的步骤是:window -> preferences -> XML -> XML CataLog ->Add    Location中选定我们自定义的xsd路径。key那里把我们的xsd文件地址放入。例:http://www.back.com/schema/back-service.xsd




	
	

三、扩展BeanDefinitionParser建立自定义标签解析成为我们自定义bean的过程。

1、 NamespaceHandlerSupport接口扩展,指定真正解析的类。根据xsd文件中的Element的name作为key,value就是我们第二个要扩展的真正解析的类

public class BackServiceNamespaceHandler extends NamespaceHandlerSupport{

	@Override
	public void init() {
		registerBeanDefinitionParser("service", new ProviderFactoryBeanDefinitionParser());
	}
}

2、AbstractSingleBeanDefinitionParser扩展。解析自定义标签的属性。然后将属性植入IOC创建的对象中。

public class ProviderFactoryBeanDefinitionParser extends AbstractSingleBeanDefinitionParser{

	@Override
	protected Class getBeanClass(Element element) {
		return ProviderFactoryBean.class;
	}
	
	@Override
	protected void doParse(Element element, BeanDefinitionBuilder builder) {
		try {
			String timeout = element.getAttribute("timeout");
			String serviceInterface = element.getAttribute("interface");
			String servicePort = element.getAttribute("servicePort");
			String ref = element.getAttribute("ref");
			String weight = element.getAttribute("weight");
			String workerThread = element.getAttribute("workerThread");
			String appKey = element.getAttribute("appKey");
			String groupName = element.getAttribute("groupName");
			
			builder.addPropertyValue("timeout", Integer.parseInt(timeout));
			builder.addPropertyValue("servicePort", Integer.parseInt(servicePort));
			builder.addPropertyValue("serviceInterface", Class.forName(serviceInterface));
			builder.addPropertyReference("serviceObject", ref);
			builder.addPropertyValue("appKey", appKey);
			if(NumberUtils.isNumber(weight)){
				builder.addPropertyValue("weight", Integer.parseInt(weight));
			}
			if(NumberUtils.isNumber(workerThread)){
				builder.addPropertyValue("workerThread", Integer.parseInt(workerThread));
			}
			if(StringUtils.isNotBlank(groupName)){
				builder.addPropertyValue("groupName", groupName);
			}
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e);
		}
	}
}

3、xsd文件解析必须创建两个文件。作用是schame指定本地文件、xsd指定对应的namespaceHandler类

1)resources/META-INF下新建文件spring.handlers  内容如下

http\://www.back.com/schema/back-service=com.back.spring.provider.BackServiceNamespaceHandler

2)resources/META-INF下新建文件spring.schemas  内容如下

http\://www.back.com/schema/back-service.xsd=META-INF/back-service.xsd

 

Ok,做到上面的几部其实就把我们需要的类整合进入Spring了,类似Dubbo等框架所说的整合Spring就是在做这些事,开发者只需要在Spring中使用自定义标签或者自定义类,就能完成自己想做的事了。

最后我们写个测试类来测试下上面的xml中的配置加载成为bean后是否帮我们做了向注册中心注册的动作(注册中心代码在上一篇博客中)。

public class TestSpringXsd {

	
	public static void main(String[] args) throws InterruptedException {
		
		ClassPathResource resource = new ClassPathResource("spring.xml");
		DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
		XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
		reader.loadBeanDefinitions(resource); 
		beanFactory.getBean("aaa");
		
		TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
	}
}

我们去观察下对应地址的zookeeper,发现如下:接口地址下发现已经将ip和端口以临时节点的形式注册到zookeeper

你可能感兴趣的:(RPC)