问题一:
以@JsonRpc注解的接口必须有value值,而且value值必须不能以 /开头,类似于这样:
@JsonRpcService("jsonRpc/products") 这里注意,不能以 /开头,后面修改源码解决这个问题
public interface IProductRpcService {
/**
* 根据请求参数,获取请求内容
* @param req
* @return
*/
List findProduct(ProductRpcReq req);
/**
* 根据id获取产品
* @param id
* @return
*/
Product findProductById(String id);
}
问题二:
调用远端接口的baseUrl必须以/ 结尾,类似于这样:
rpc:
client:
url: http://localhost:8081/manager
basePackage: com.chukun.api.rpc
这两个问题,都是新手比较容易犯的错误,现在优化,@JsonRpcService不写,默认是接口的全类名,baseUrl不以 /结尾,自动添加
@JsonRpcService在服务端优化:
1.修改注解的默认值:
@Target(TYPE)
@Retention(RUNTIME)
public @interface JsonRpcService {
/**
* The path that the service is available at.
*
* @return the service path
*/
String value() default ""; 添加默认值
}
2.找到AutoJsonRpcServiceImplExporter类,进入,发现这个类实现了spring的后置处理器,找到实现的方法:
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
//此方法获取bean的定义信息
Map servicePathToBeanName = findServiceBeanDefinitions(defaultListableBeanFactory);
for (Entry entry : servicePathToBeanName.entrySet()) {
registerServiceProxy(defaultListableBeanFactory, makeUrlPath(entry.getKey()), entry.getValue());
}
}
3.进入findServiceBeanDefinitions方法:发现在此方法中,添加注解的value值:
private static Map findServiceBeanDefinitions(ConfigurableListableBeanFactory beanFactory)
{
Map serviceBeanNames = new HashMap();
String beanName;
for (beanName : beanFactory.getBeanDefinitionNames())
{
AutoJsonRpcServiceImpl autoJsonRpcServiceImplAnnotation = (AutoJsonRpcServiceImpl)beanFactory.findAnnotationOnBean(beanName, AutoJsonRpcServiceImpl.class);
JsonRpcService jsonRpcServiceAnnotation = (JsonRpcService)beanFactory.findAnnotationOnBean(beanName, JsonRpcService.class);
if (null != autoJsonRpcServiceImplAnnotation)
{
if (null == jsonRpcServiceAnnotation) {
throw new IllegalStateException("on the bean [" + beanName + "], @" + AutoJsonRpcServiceImpl.class.getSimpleName() + " was found, but not @" + JsonRpcService.class.getSimpleName() + " -- both are required");
}
List paths = new ArrayList();
Collections.addAll(paths, autoJsonRpcServiceImplAnnotation.additionalPaths());
//paths.add(jsonRpcServiceAnnotation.value()); //添加注解的value值
修改如下:
paths.add(getCurrentRpcServiceFullClassName((DefaultListableBeanFactory) beanFactory,beanName));
for (String path : paths)
{
if (!PATTERN_JSONRPC_PATH.matcher(path).matches()) {
throw new RuntimeException("the path [" + path + "] for the bean [" + beanName + "] is not valid");
}
logger.info("exporting bean [{}] ---> [{}]", beanName, path);
if (isNotDuplicateService(serviceBeanNames, beanName, path)) {
serviceBeanNames.put(path, beanName);
}
}
}
}
collectFromParentBeans(beanFactory, serviceBeanNames);
return serviceBeanNames;
}
paths.add(jsonRpcServiceAnnotation.value()); 重写一个方法,让它添加类的全类名:
/**
* 获取当前jsonRpc接口的全类名
* Registers the new beans with the bean factory.
*/
private static String getCurrentRpcServiceFullClassName(DefaultListableBeanFactory defaultListableBeanFactory, String serviceBeanName) {
BeanDefinition serviceBeanDefinition = findBeanDefinition(defaultListableBeanFactory, serviceBeanName);
String serviceInterface = "";
for (Class> currentInterface : getBeanInterfaces(serviceBeanDefinition, defaultListableBeanFactory.getBeanClassLoader())) {
if (currentInterface.isAnnotationPresent(JsonRpcService.class)) {
serviceInterface = currentInterface.getName();
break;
}
}
return serviceInterface;
}
这样注解就不用写value值,默认是接口的全类名
2.优化baseUrl不以 /结尾,自动添加
客户端优化,找到此类:AutoJsonRpcClientProxyCreator,此类也是实现spring的后置处理器,
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
SimpleMetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(applicationContext);
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
String resolvedPath = resolvePackageToScan();
logger.debug("Scanning '{}' for JSON-RPC service interfaces.", resolvedPath);
try {
for (Resource resource : applicationContext.getResources(resolvedPath)) {
if (resource.isReadable()) {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
ClassMetadata classMetadata = metadataReader.getClassMetadata();
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
String jsonRpcPathAnnotation = JsonRpcService.class.getName();
if (annotationMetadata.isAnnotated(jsonRpcPathAnnotation)) {
String className = classMetadata.getClassName();
String path = className;
logger.debug("Found JSON-RPC service to proxy [{}] on path '{}'.", className, path);
//此方法,注册调用路径
registerJsonProxyBean(defaultListableBeanFactory, className, path);
}
}
}
} catch (IOException e) {
throw new IllegalStateException(format("Cannot scan package '%s' for classes.", resolvedPath), e);
}
}
进入registerJsonProxyBean(defaultListableBeanFactory, className, path)方法,发现:
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
.rootBeanDefinition(JsonProxyFactoryBean.class)
.addPropertyValue("serviceUrl", appendBasePath(path))
.addPropertyValue("serviceInterface", className);
appendBasePath(path)添加调用路径,继续进入此方法,发现此方法的实现很简单:
private String appendBasePath(String path)
{
try
{
return new URL(this.baseUrl, path).toString();//重写
}
catch (MalformedURLException e)
{
throw new IllegalArgumentException(String.format("Cannot combine URLs '%s' and '%s' to valid URL.", new Object[] { this.baseUrl, path }), e);
}
}
重写此方法:
/**
* 解决配置prc接口不以 /结尾报错
* Appends the base path to the path found in the interface.
*/
private String appendBasePath(String path) {
String baseUrlStr = baseUrl.toString();
if(!baseUrlStr.endsWith("/")){
baseUrlStr = baseUrlStr.concat("/");
}
return baseUrlStr.concat(path);
}