从零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服务的开启、注册中心的注册。装饰类来装饰对应的每个接口的实现类。

/**
 * 
 * <p>Title: ProviderFactoryBean.java</p>  
* <p>Description: </p>  
* @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<ProviderService> serviceMetaData = buildProviderServiceInfo();
		RegisterCenter.singleton().registerProvider(serviceMetaData);
	}

	private List<ProviderService> buildProviderServiceInfo() {
		List<ProviderService> 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,找对应的解析类。再下面就是属性名称和类型的定义。相信一目了然,不再介绍。

<xsd:schema xmlns="http://www.back.com/schema/back-service"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
	xmlns:beans="http://www.springframework.org/schema/beans"
	targetNamespace="http://www.back.com/schema/back-service" 
		elementFormDefault="qualified" attributeFormDefault="unqualified">
	
	<xsd:import namespace="http://www.springframework.org/schema/beans"/>

	<xsd:element name="service">
	<xsd:complexType>
	<xsd:complexContent>
		<xsd:extension base="beans:identifiedType">
			<xsd:attribute name="interface" type="xsd:string" use="required"/>
			<xsd:attribute name="timeout" type="xsd:int" use="required"/>
			<xsd:attribute name="servicePort" type="xsd:int" use="required"/>
			<xsd:attribute name="ref" type="xsd:string" use="required"/>
			<xsd:attribute name="weight" type="xsd:int" use="optional"/>
			<xsd:attribute name="workerThreads" type="xsd:int" use="optional"/>
			<xsd:attribute name="appKey" type="xsd:string" use="required"/>
			<xsd:attribute name="groupName" type="xsd:string" use="optional"/>
		</xsd:extension>
	</xsd:complexContent>
	</xsd:complexType>
	</xsd:element>
</xsd:schema>

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

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:backService="http://www.back.com/schema/back-service" 
    xmlns:backReference="http://www.back.com/schema/back-reference" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.back.com/schema/back-service 
        http://www.back.com/schema/back-service.xsd
        http://www.back.com/schema/back-reference
        http://www.back.com/schema/back-reference.xsd" >

	<bean id="serRemote" class="com.back.spring.test.RemoteServiceImpl" />
	<backService:service id="aaa" interface="com.back.spring.test.RemoteService" timeout="10"
	servicePort="9999" ref="serRemote" appKey="dodp" groupName="unps"/>

</beans>

三、扩展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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值