基于HTTP实现
# 直接使用RestTemplate
引入RestTemplate的Bean:
@Configuration public class RestConfig { @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } }
1
2
3
4
5
6
7在consumer中远程调用:
@RestController @RequestMapping("/consumer") public class ConsumerController { @Autowired private RestTemplate restTemplate; @GetMapping("/find/{id}") public Goods find(@PathVariable Long id){ ResponseEntity<Goods> forEntity = restTemplate.getForEntity("http://localhost:9527/provider/goods/1", Goods.class); if (forEntity.getStatusCode().is2xxSuccessful()){ return forEntity.getBody(); } return null; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 基于自定义注解
# 自定义注解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface NrealHttpClient { String value(); }
1
2
3
4
5
6@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface NrealMapping { //api路径 String api() default ""; //调用的主机和端口 String url() default ""; }
1
2
3
4
5
6
7
8
9
10
# 注解接口Bean
将添加了 @NrealHttpClient 的接口生成一个代理类的 Bean,交给spring管理;
动态代理
每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,并且每个代理类的实例都关联到了实现该接口的动态代理类调用处理程序中,当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现 InvocationHandler接口类的 invoke方法来调用;
public class NrealHttpClientProxy implements InvocationHandler { public NrealHttpClientProxy(){ } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { NrealMapping annotation = method.getAnnotation(NrealMapping.class); if (annotation != null){ String url = annotation.url(); // /provide/goods/{id} String api = annotation.api(); Pattern compile = Pattern.compile("(\\{\\w+})"); Matcher matcher = compile.matcher(api); if (matcher.find()){ //简单判断一下 代表有路径参数需要替换 int groupCount = matcher.groupCount(); for (int i=0;i<groupCount;i++){ String group = matcher.group(i); api = api.replace(group,args[i].toString()); } } RestTemplate restTemplate = new RestTemplate(); return restTemplate.getForObject(url+api,method.getReturnType()); } return null; } public <T> T getProxy(Class<T> interfaceClass) { return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),new Class<?>[]{interfaceClass},this); } public static void main(String[] args) { Pattern compile = Pattern.compile("(\\{\\w+})"); Matcher matcher = compile.matcher("test/test/{id}"); System.out.println(matcher.find()); } }
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生成Bean
将接口代理注册到 spring容器中,需要使用 FactoryBean的实现类,所有的类都是通过 bean工厂生产的;
public class NrealHttpClientFactoryBean<T> implements FactoryBean<T> { private Class<T> interfaceClass; @Override public T getObject() throws Exception { //返回一个代理实现类 return new NrealHttpClientProxy().getProxy(interfaceClass); } //类型是接口 @Override public Class<?> getObjectType() { return interfaceClass; } @Override public boolean isSingleton() { return true; } public Class<T> getInterfaceClass() { return interfaceClass; } public void setInterfaceClass(Class<T> interfaceClass) { this.interfaceClass = interfaceClass; } }
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
30Bean定义注册
ImportBeanDefinitionRegistrar接口:
ImportBeanDefinitionRegistrar类只能通过其他类 @Import的方式来加载,通常是启动类或配置类;
使用 @Import,如果括号中的类是 ImportBeanDefinitionRegistrar的实现类,则会调用接口方法,将其中要注册的类注册成 bean;
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(NrealBeanDefinitionRegistry.class) public @interface EnableHttpClient { //扫包路径 String basePackage(); }
1
2
3
4
5
6
7
8实现该接口的类拥有注册 bean的能力;
public class NrealBeanDefinitionRegistry implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware { private Environment environment; private ResourceLoader resourceLoader; @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { registerNrealHttpClient(metadata,registry); } private void registerNrealHttpClient(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //将NrealHttpClient所标识的接口,生成代理类,并且注册到spring容器当中 Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(EnableHttpClient.class.getCanonicalName()); Object basePackage = annotationAttributes.get("basePackage"); if (basePackage != null){ String base = basePackage.toString(); //ClassPathScanningCandidateComponentProvider是Spring提供的工具,可以按自定义的类型,查找classpath下符合要求的class文件 ClassPathScanningCandidateComponentProvider scanner = getScanner(); scanner.setResourceLoader(resourceLoader); //找到NrealHttpClient 扫描器 就会进行扫描 AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(NrealHttpClient.class); scanner.addIncludeFilter(annotationTypeFilter); //包路径 com.Nreal.rpc 路径下进行扫描 Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(base); for (BeanDefinition candidateComponent : candidateComponents) { //BeanDefinition spring bean定义 if (candidateComponent instanceof AnnotatedBeanDefinition){ AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) candidateComponent; AnnotationMetadata annotationMetadata = annotatedBeanDefinition.getMetadata(); Assert.isTrue(annotationMetadata.isInterface(),"MsHttpClient注解必须定义在接口上"); Map<String, Object> httpClientAttributes = annotationMetadata.getAnnotationAttributes(NrealHttpClient.class.getCanonicalName()); //获取注解当中的value值,这个value值 是我们的 bean的名称 String beanName = getClientName(httpClientAttributes); //接口无法实例化,所以生成代理实现类 BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(NrealHttpClientFactoryBean.class); beanDefinitionBuilder.addPropertyValue("interfaceClass",annotationMetadata.getClassName()); registry.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition()); } } } } private String getClientName(Map<String, Object> clientAnnotationAttributes) { if (clientAnnotationAttributes == null){ throw new RuntimeException("value必须有值"); } Object value = clientAnnotationAttributes.get("value"); if (value != null && !value.toString().equals("")){ return value.toString(); } return null; } //这个方法是从Feign组件中源码找的 protected ClassPathScanningCandidateComponentProvider getScanner() { return new ClassPathScanningCandidateComponentProvider(false, this.environment) { @Override protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { boolean isCandidate = false; if (beanDefinition.getMetadata().isIndependent()) { if (!beanDefinition.getMetadata().isAnnotation()) { isCandidate = true; } } return isCandidate; } }; } @Override public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } }
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# Consumer模块测试
做一个配置类,开启 NrealHttpClient的支持,设置扫包路径;
@Configuration @EnableHttpClient(basePackage = "com.Nreal.rpc") public class RpcConfig { }
1
2
3
4调用provider 接口;
@NrealHttpClient(value = "goodsHttpRpc") public interface GoodsHttpRpc { @NrealMapping(url="http://localhost:9527",api="/provider/goods/{id}") public Goods findGoods(@PathVariable Long id); }
1
2
3
4
5Controller接口测试;
@Autowired private GoodsHttpRpc goodsHttpRpc; @GetMapping("/find/{id}") public Goods find(@PathVariable Long id){ return goodsHttpRpc.findGoods(id); }
1
2
3
4
5
6
7