通过@Service
的定义可以发现,这个注解可以作用在类上
从dubbo的角度说来:@Service
作用的类会去spring中注册成单例bean,只不是需要增强一下。这里同样可以将@Component
作为元注解使用
这里的增强其实只是要把有@Service
标记的类按照/classFullName/methodName
格式去vertx的路由里注册,能进行http访问即可
实际上这里使用BeanPostProcessor
在实例完全初始化完成之后去执行添加路由就可以了
@Reference定义
定义里的属性出了value都可以只做为占位符,大部分都是没有什么用处的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.alibaba.dubbo.config.annotation;
import org.springframework.core.annotation.AliasFor; import org.springframework.stereotype.Component;
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Service { @AliasFor(annotation = Component.class) String value() default "";
...... }
|
BeanPostProcessor
之前说到InstantiationAwareBeanPostProcessor
是BeanPostProcessor
的子接口,但是提供了实例化前的能力
BeanPostProcessor
是在实例化之后、各种Aware配置之后,围绕afterPropertiesSet
和initMethod
前后的扩展点。有两个自己定义的方法
- postProcessBeforeInitialization:在实例init之前的执行,返回输入的bean或者包装过的bean,如果为空不会调用其它的
BeanPostProcessor
- postProcessAfterInitialization:在实例init之后的执行,返回结果同上
具体实现
@Service
的实现要比@Reference
简单的多,实现postProcessAfterInitialization
即可
这里只需要注意,其它系统只知道接口名而不是实现类名,所以要获取到接口名
- 查看每个bean是否有定义
@Service
- 如果有,这个bean只实现一个接口,则返回接口类全名;否则获取方法最多的接口,并返回这个接口类全名
- 将这些接口和
@Service
定义加入缓存,在spring完全启动之后注册到vertx的路由里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public class DubboBeanPostProcessor implements BeanPostProcessor {
@Override public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException { Class<?> clazz = bean.getClass(); Service service = clazz.getDeclaredAnnotation(Service.class); if (null != service) { Class<?> most = getMostAvailableInterface(clazz); if (null != most) { DubboRouterCache.addService(most, service); } } return bean; }
private Class<?> getMostAvailableInterface(Class<?> clazz) { Class<?>[] interfaces = clazz.getInterfaces(); int length = interfaces.length; if (length == 0) { return null; } if (length == 1) { return interfaces[0]; }
String simple = clazz.getSimpleName(); for (Class<?> one : interfaces) { if (simple.startsWith(one.getSimpleName())) { return one; } } int max = -1; Class<?> most = null; for (Class<?> one : interfaces) { int num = one.getDeclaredMethods().length; if (num > max) { max = num; most = one; } } return most; } }
|
注册路由
vertx同样实在spring启动之后才会启动,这里的Router
实在vertx启动时,主动调用传进来的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| @Override public void buildRouter(Router router) { final Map<Class<?>, Annotation> allService = DubboRouterCache.getAllService(); if (CollectionUtil.isEmpty(allService)) { return; } final ConfigurableApplicationContext context = Bottle.obtain(ConfigurableApplicationContext.class); for (Class<?> serviceClass : allService.keySet()) { final Map<String, ?> beans = context.getBeansOfType(serviceClass); if (CollectionUtil.isEmpty(beans)) { throw new ShutdownException("Interface: " + serviceClass + " does not implement class"); } if (beans.size() > 1) { throw new ShutdownException("Interface: " + serviceClass + " has multiple implement classes"); } final Iterator<?> iterator = beans.values().iterator(); buildRouter(router, serviceClass, iterator.next()); } }
private void buildRouter(Router router, Class<?> serviceClass, Object serviceImpl) { final Method[] declaredMethods = serviceClass.getDeclaredMethods(); final String serviceName = serviceClass.getName(); final Set<String> methodNames = CollectionUtil.newHashSet(declaredMethods.length); for (Method method : declaredMethods) { methodNames.add(method.getName()); try { MethodInvoker.warmup(serviceImpl, method); } catch (Throwable err) { throw new InnerException("Build route failed, can not reflect methods"); } } for (String methodName : methodNames) { try { router.post("/" + serviceName + "/" + methodName).blockingHandler(ctx -> { }); } catch (Throwable err) { throw new InnerException("Build route failed, not found methods"); } } }
|