1. Today we are going to implement three functions of dubbo:
- Definition of dubbo tags
- The dubbo tag is parsed and assigned to the corresponding XXConfig (eg: ServiceConfig corresponds to the dubbo:service tag)
- How dubbo uses the life cycle of spring bean to export dubbo:service services
2. Code
implementation As we all know, spring provides a function that allows us to customize labels (don't understand, google it yourself), dubbo uses it like this:
create three files (file content, which will be downloaded later):
src/META -INF/dubbo.xsd
src/META-INF/spring.handlers
src/META-INF/spring.schemas
When spring initializes a custom label, it will call back the init method of NamespaceHandlerSupport
/***
* spring 解析 <dubbo:> 标签执行
*
* @author HadLuo
* @since JDK1.7
* @history 2018年5月2日 新建
*/
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
// <dubbo:application> 标签
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationBean.class));
// <dubbo:registry> 标签
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryBean.class));
// <dubbo:protocol> 标签
registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolBean.class));
// <dubbo:service> 标签
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class));
}
}
DubboBeanDefinitionParser parser, spring will automatically call the parse method
public class DubboBeanDefinitionParser implements BeanDefinitionParser {
private Class<?> beanClass;
private static final DubboIdStrategy ID_STRATEGY = new DubboIdStrategy();
public DubboBeanDefinitionParser(Class<?> beanClass) {
this.beanClass = beanClass;
}
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 动态注入 spring bean
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClass(beanClass);
beanDefinition.setLazyInit(false);
String id = element.getAttribute("id");
if (id == null || id.isEmpty()) {
// dubbo标签中没有id属性的 , 生成一个不重复的id
id = IdGenerator.generateId(ID_STRATEGY, beanClass);
}
if (parserContext.getRegistry().containsBeanDefinition(id)) {
// spring 有这个id 的bean 了 直接抛出异常
throw new IllegalStateException("Duplicate spring bean id " + id);
}
// 将 dubbo标签bean 注入spring
parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
// 为 dubbo标签bean设置属性值 , 这里不同于dubbo源码(源码太繁琐了), 这里直接 用反射 设置基本类型值,利用holder来设置 属性为 dubbo bean对象 的值
for (Field field : ReflectUtils.getAllFieldsByAnnotation(beanClass, Tag.class)) {
String tag = field.getAnnotation(Tag.class).value();
Class<?> holderClass = field.getAnnotation(Tag.class).holder();
beanDefinition.getPropertyValues().addPropertyValue(field.getName(),
getValueByType(field, element.getAttribute(tag),holderClass));
}
return beanDefinition;
}
/***
* 设置字段值
*
* @param field
* @param target
* @param val
* @throws NumberFormatException
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @author HadLuo 2018年4月13日 新建
*/
@SuppressWarnings("rawtypes")
private static Object getValueByType(Field field, String val,Class<?> holderClass) {
if (val == null) {
return null;
}
if (field.getType().equals(Integer.class) || field.getType().equals(int.class)) {
return Integer.parseInt(val);
} else if (field.getType().equals(Boolean.class) || field.getType().equals(boolean.class)) {
return Boolean.parseBoolean(val);
} else if (field.getType().equals(Byte.class) || field.getType().equals(byte.class)) {
return Byte.parseByte(val);
} else if (field.getType().equals(Character.class) || field.getType().equals(char.class)) {
return val.charAt(0);
} else if (field.getType().equals(Double.class) || field.getType().equals(double.class)) {
return Double.parseDouble(val);
} else if (field.getType().equals(Float.class) || field.getType().equals(float.class)) {
return Float.parseFloat(val);
} else if (field.getType().equals(Long.class) || field.getType().equals(long.class)) {
return Long.parseLong(val);
} else if (field.getType().equals(Short.class) || field.getType().equals(short.class)) {
return Short.parseShort(val);
} else if (field.getType().equals(String.class)) {
return val;
} else if (ReflectUtils.isChild(field.getType(), Holder.class)) {
// 代表 是 对象 类型 , 从spring里面 找
String id = ID_STRATEGY.getFixBeanId(holderClass);
if (id == null || id.isEmpty()) {
throw new UnsupportedOperationException(field.getType().getName() + " 不能赋值为 :" + val);
}
// 用holder来 占位 , 后面初始化时 ,直接 利用spring context根据id来找dubbo bean
return new Holder(id);
}
throw new UnsupportedOperationException(field.getType().getName() + " 不能赋值为 :" + val);
}
}
ServiceBean implements several life cycle methods of spring bean and inherits ServiceConfig (configuration value of service tag)
public class ServiceBean extends ServiceConfig implements SpringBeanLifecycle {
// 是否已经导出 ServiceBean 服务
private transient boolean exported;
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
if (exported) {
return;
}
setExported(true);
export();
}
}
@Override
public void afterPropertiesSet() throws Exception {
System.err.println("spring init " + ServiceBean.class.getName() + " bean");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
registerApplicationContext(applicationContext);
}
@Override
public void setBeanName(String name) {
}
public synchronized void setExported(boolean exported) {
this.exported = exported;
}
public synchronized boolean exported() {
return exported;
}
@Override
public void destroy() throws Exception {
setApplicationContext(null);
setExported(false);
}
}
ServiceConfig configuration information class (including implementation exports).
public class ServiceConfig extends AbstractConfig {
/** 服务接口全路径 Tag注解,是我自己实现的 不同于dubbo*/
@Tag("interface")
private String _interface;
/** 服务对象实现引用 */
@Tag("ref")
private String ref;
/** 远程服务调用超时时间(毫秒) */
@Tag("timeout")
private long timeout = 1000;
/** 负载均衡策略,可选值为:random(随机)、roundrobin(轮询)、leastactive(最少活跃调用) */
@Tag("loadbalance")
private String loadbalance = "random";
/** 是否缺省异步执行,不可靠的异步,只是忽略返回值,不阻塞执行线程 */
@Tag("async")
private boolean async = false;
/***holder 注入, 复杂对象****/
@Tag(holder = ApplicationBean.class)
private Holder<ApplicationBean> applicationHolder;
/***holder 注入, 复杂对象****/
@Tag(holder = ProtocolBean.class)
private Holder<ProtocolBean> protocolHolder;
public String getInterface() {
return _interface;
}
public String getRef() {
return ref;
}
public long getTimeout() {
return timeout;
}
public String getLoadbalance() {
return loadbalance;
}
public boolean isAsync() {
return async;
}
public void set_interface(String _interface) {
this._interface = _interface;
}
public void setRef(String ref) {
this.ref = ref;
}
public void setTimeout(long timeout) {
this.timeout = timeout;
}
public void setAsync(boolean async) {
this.async = async;
}
public void setApplicationHolder(Holder<ApplicationBean> applicationHolder) {
this.applicationHolder = applicationHolder;
}
public void setProtocolHolder(Holder<ProtocolBean> protocolHolder) {
this.protocolHolder = protocolHolder;
}
public void setLoadbalance(String loadbalance) {
this.loadbalance = loadbalance;
}
// dubbo://10.112.6.12:20880/cn.javacoder.test.dubbo.IHelloWorldService?application=test-provider
// &dubbo=2.5.3&interface=cn.javacoder.test.dubbo.IHelloWorldService&methods=say&pid=6816&side=provider×tamp=1522284835101
public void export() {
if (getApplicationContext() == null) {
throw new RuntimeException("spring 还没有初始化完成");
}
wakeupHolder();
// 这里 模拟 ======================
String url = "dubbo://" + "10.112.6.12:" + protocolHolder.get().getPort() + _interface + "?"
+ "application=" + applicationHolder.get().getName() + "&dubbo=2.5.3&interface=" + _interface
+ "&methods=say&pid=6816&side=provider×tamp=1522284835101";
System.err.println("准备开始 导出 >>" + url);
}
private void wakeupHolder() {
// 利用spring context 根据id 来找 dubbo bean标签
applicationHolder.setValue(getApplicationContext());
protocolHolder.setValue(getApplicationContext());
}
}
The above is the search and export of dubbo's simple service tag, which is basically the same as the idea of dubbo source code implementation. There is no project source code posted above, you can add me qq: 657455400 or download it yourself: https://download.csdn.net/download/luozheng4698729 /10387658