ServiceBeanNameBuilder
用以构建 ServcieBean 的 name,一个常见的建造者模式。ServcieBean 的 name主要有interfaceClassName、version、group组成,interfaceClassName是必有,后两者可选,比如"ServiceBean:org.com.Demo:1.0.0:lala"该类含有的属性如下:
private static final String SEPARATOR = ":";
// Required
private final String interfaceClassName;
private final Environment environment;
// Optional
private String version;
private String group;
提供了一些create静态方法用以创建ServiceBeanNameBuilder实例,然后链式调用其group(x)、version(x)方法,最后build方法进行构建。eg : ServiceBeanNameBuilder.create(x).group(x).version(x).build() 。下面看create方法。getAnnotationAttributes是spring包的AnnotationUtils类的方法,返回值为AnnotationAttributes,就是把@Service、@Reference注解的属性值填充到返回的AnnotationAttributes对象中。
public static ServiceBeanNameBuilder create(AnnotationAttributes attributes, Class<?> defaultInterfaceClass, Environment environment) {
return new ServiceBeanNameBuilder(attributes, defaultInterfaceClass, environment);
}
public static ServiceBeanNameBuilder create(Class<?> interfaceClass, Environment environment) {
return new ServiceBeanNameBuilder(interfaceClass, environment);
}
public static ServiceBeanNameBuilder create(Service service, Class<?> interfaceClass, Environment environment) {
return create(getAnnotationAttributes(service, false, false), interfaceClass, environment);// 调第一个
}
public static ServiceBeanNameBuilder create(Reference reference, Class<?> interfaceClass, Environment environment) {
return create(getAnnotationAttributes(reference, false, false), interfaceClass, environment);// 调第一个
}
三个构造方法如下,这里说下最后一个构造方法。第一个参数是spring的类(前面create方法调用getAnnotationAttributes方法的返回值传入来的)。第一步调用了DubboAnnotationUtils#resolveInterfaceName方法,该方法内部主要是获取注解的属性集合(attributes)中属性名为interfaceClass的值,可以看后面test程序的@Service(interfaceClass = DemoService.class…);既然有attributes,肯定含有group和version信息,顺带取出即可。
private ServiceBeanNameBuilder(Class<?> interfaceClass, Environment environment) {
this(interfaceClass.getName(), environment);// 调用下面的
}
private ServiceBeanNameBuilder(String interfaceClassName, Environment environment) {
this.interfaceClassName = interfaceClassName;
this.environment = environment;
}
private ServiceBeanNameBuilder(AnnotationAttributes attributes, Class<?> defaultInterfaceClass, Environment environment) {
this(resolveInterfaceName(attributes, defaultInterfaceClass), environment);// 调用上面的
this.group(getAttribute(attributes,"group"));
this.version(getAttribute(attributes,"version"));
}
如下,两个方法,建造者模式的惯用法,用以链式调用,给builder本身(非目标对象)的属性赋值。
public ServiceBeanNameBuilder group(String group) {
this.group = group;
return this;
}
public ServiceBeanNameBuilder version(String version) {
this.version = version;
return this;
}
build方法,生成目标对象,这里目标对象很简单,就是String。正常的实体类型比如Person,build内部会创建Person实例,然后挨个赋值到person对象并返回。build方法逻辑这样的,所有的name都是以"ServiceBean"开头,然后追加interfaceClassName、version、group信息,中间以SEPARATOR即:分割(在append方法内部),最后一个是防止有placeHolder,利用Environment解析填充即可,比如"ServiceBean:org.com.Demo:1.0.0:${group}"。
public String build() {
StringBuilder beanNameBuilder = new StringBuilder("ServiceBean");
// Required
append(beanNameBuilder, interfaceClassName);
// Optional
append(beanNameBuilder, version);
append(beanNameBuilder, group);
// Build and remove last ":"
String rawBeanName = beanNameBuilder.toString();
// Resolve placeholders
return environment.resolvePlaceholders(rawBeanName);
}
ServiceBeanNameBuilderTest
测试程序很简单。prepare方法就是MockEnvironment,之前讲过了, @Reference注解里面正好有${dubbo.version}这个,MockEnvironment就可以解析并替换为"1.0.0"
两个测试方法分别测试 类上的@Service注解和INTERFACE_CLASS属性上的 @Reference注解,两个测试方法使用ServiceBeanNameBuilder.create + build完成String ServiceBeanName 的构建 。注意两个注解的interfaceClass、group、version三个属性就好。(注意ServiceBeanNameBuilder#build()方法最后会调用environment的resolvePlaceholders方法,将@Reference注解的${dubbo.version}属性值替换为"1.0.0")
@Service(interfaceClass = DemoService.class, group = GROUP, version = VERSION,
application = "application", module = "module", registry = {"1", "2", "3"})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class ServiceBeanNameBuilderTest {
@Reference(interfaceClass = DemoService.class, group = "DUBBO", version = "${dubbo.version}",
application = "application", module = "module", registry = {"1", "2", "3"})
static final Class<?> INTERFACE_CLASS = DemoService.class;
static final String GROUP = "DUBBO";
static final String VERSION = "1.0.0";
private MockEnvironment environment;
@BeforeEach
public void prepare() {
environment = new MockEnvironment();
environment.setProperty("dubbo.version", "1.0.0");
}
@Test
public void testServiceAnnotation() {
Service service = AnnotationUtils.getAnnotation(ServiceBeanNameBuilderTest.class, Service.class);
ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(service, INTERFACE_CLASS, environment);
Assertions.assertEquals("ServiceBean:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO",
builder.build());
}
@Test
public void testReferenceAnnotation() {
Reference reference = AnnotationUtils.getAnnotation(ReflectionUtils.findField(ServiceBeanNameBuilderTest.class, "INTERFACE_CLASS"), Reference.class);
ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(reference, INTERFACE_CLASS, environment);
Assertions.assertEquals("ServiceBean:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO",
builder.build());
}
}