前言
FeignClient的實現原理網上一搜一大把,此處我就不詳細再說明,比如:Feign原理 (圖解) - 瘋狂創客圈 - 部落格園 (cnblogs.com),而且關於FeignClient的使用技巧我之前文章《feignclient各種使用技巧說明》已經講過,此處僅說一下核心步驟:
啟動時:@EnableFeignClients註解-->@Import(FeignClientsRegistrar.class)-->FeignClientsRegistrar.registerBeanDefinitions-->org.springframework.cloud.netflix.feign.FeignClientsRegistrar#registerFeignClients-->掃描有新增了@FeignClient註解的介面類註解BEAN元資訊列表【即:AnnotatedBeanDefinition】-->org.springframework.cloud.netflix.feign.FeignClientsRegistrar#registerFeignClient-->構建一個FeignClientFactoryBean的BeanDefinitionBuilder,並將type等相關資訊設定給FeignClientFactoryBean,-->BeanDefinitionReaderUtils.registerBeanDefinition【即註冊成FactoryBean】;
實際注入FeignClient介面類依賴時:根據FeignClient介面類class找到FeignClientFactoryBean物件例項-->org.springframework.cloud.netflix.feign.FeignClientFactoryBean#getObject-->org.springframework.cloud.netflix.feign.FeignClientFactoryBean#feign【得到Feign.Builder】-->targeter = get(context, Targeter.class);-->targeter.target-->feign.target(target)-->feign.Feign.Builder#build-->feign.ReflectiveFeign#newInstance-->handler = factory.create(target, methodToHandler)【得到InvocationHandler】
執行時:(feign.hystrix.HystrixInvocationHandler【feign.hystrix.enabled=true時】 OR feign.ReflectiveFeign.FeignInvocationHandler#)#invoke -->dispatch.get(method).invoke(args);【得到代理方法SynchronousMethodHandler並執行該方法】-->Client#execute【Client的實現類,其中:LoadBalancerFeignClient 是使用ribbon元件時預設實現的】
上面核心步驟其實也還是很多,我這裡一句概括核心:將@FeignClient標註的介面類通過FeignClientFactoryBean生成代理類(InvocationHandler,注意有多種實現子類),再執行InvocationHandler.invoke方法,間接執行內部的MethodHandler(SynchronousMethodHandler實現類之一)invoke方法,最後由實際的Client來完成遠端URL請求及響應結果轉換;其中最重要也是複雜的是InvocationHandler的實現類、MethodHandler的實現類;
FeignClient的擴充套件點非常多,比如:FeignClientsConfiguration 類中所有預設配置均可以自行替換自定義的實現類,若需單個FeignClient生效,則可通過@FeignClient註解的configuration屬性指明對應這個FeignClient的特有配置類(如:MyFeignClientConfiguration)【注意自定義的配置類此處不能使用@Configuration註解,否則將導致全域性生效,不加@Configuration註解時,則會由對應的contextId的FeignClientContext單獨建立】
那麼說了這麼多,為了大家能夠理解FeignClient的核心實現原理,同時因為我專案中也要實現類似的功能(目的讓開發人員對複雜部份透明,呼叫遠端BEAN的方法就像調本地一樣,即RPC的初衷),我(夢在旅途 www.zuowenjun.cn)通過實現仿照FeignClient框架原理的示例來看清FeignClient的本質,程式碼全部貼出來了,大家應該一看就懂,不懂複製到DEMO專案中DEBUG起來就也就明白了。實際執行的結果符合預期;
1. 定義註解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DemoClient {
//定義相關的註解屬性
}
2. 定義標註了@DemoClient註解介面對應的真實代理類FactoryBean
public class DemoClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
private Class<?> type;
private ApplicationContext context;
//可新增其它屬性欄位(同時記得至少新增setter方法)
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
@Override
public Object getObject() throws Exception {
return Proxy.newProxyInstance(RemoteTestClientFactoryBean.class.getClassLoader(), new Class<?>[]{this.type}, 自定義代理物件處理類【需繼承自InvocationHandler,也可以直接採用lambda表示式】,這裡也是真正的執行業務邏輯的核心);
}
@Override
public Class<?> getObjectType() {
return this.type;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void afterPropertiesSet() throws Exception {
}
/**
* 設定當前工廠要生成的BEAN Type,由BeanWrapper動態賦值,內部支援根據class String直接轉換為Class物件
*
* @param type
*/
public void setType(Class<?> type) {
this.type = type;
}
}
3. 定義掃描標註了@DemoClient註解介面並自動註冊為如上第2步定義的FactoryBean的Bean
public class DemoClientsRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private Environment environment;
@Override
public void setEnvironment(Environment environment) {
this.environment=environment;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
ClassPathScanningCandidateComponentProvider scanner=getScanner();
scanner.addIncludeFilter(new AnnotationTypeFilter(RemoteTestClient.class));
Set<BeanDefinition> candidateComponents=scanner.findCandidateComponents(ClassUtils.getPackageName(Application.class));
if (CollectionUtils.isEmpty(candidateComponents)){
return;
}
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata metadata = beanDefinition.getMetadata();
Assert.isTrue(metadata.isInterface(),
"@DemoClient can only be specified on an interface");
registerDemoClient(beanDefinitionRegistry,metadata);
}
}
}
private void registerDemoClient(BeanDefinitionRegistry registry,AnnotationMetadata annotationMetadata){
String className = annotationMetadata.getClassName();
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(DemoClientFactoryBean.class);
//這裡將類名傳給class<?>屬性,看似型別不匹配,其實賦值時會由BeanWrapper進行自動轉換
definition.addPropertyValue("type",className);
//這裡還可以賦值更多屬性,具體依據DemoClientFactoryBean的屬性定義
registry.registerBeanDefinition(className + "DemoClient",definition.getBeanDefinition());
}
protected ClassPathScanningCandidateComponentProvider getScanner() {
return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
@Override
protected boolean isCandidateComponent(
AnnotatedBeanDefinition beanDefinition) {
//必需是獨立的 且 是介面 才是符合掃描條件的【可以更嚴格的過濾判斷】
if (beanDefinition.getMetadata().isIndependent() && beanDefinition.getMetadata().isInterface()) {
if (beanDefinition.getMetadata().isInterface()
&& beanDefinition.getMetadata()
.getInterfaceNames().length == 1
&& Annotation.class.getName().equals(beanDefinition
.getMetadata().getInterfaceNames()[0])) {
try {
Class<?> target = ClassUtils.forName(
beanDefinition.getMetadata().getClassName(),
RemoteTestClientsRegistrar.class.getClassLoader());
return !target.isAnnotation();
}
catch (Exception ex) {
this.logger.error(
"Could not load target class: "
+ beanDefinition.getMetadata().getClassName(),
ex);
}
}
return true;
}
return false;
}
};
}
}
4.定義配置啟動掃描並註冊代理Bean的註解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DemoClientsRegistrar.class)
public @interface EnableDemoClient {
}
5.最後將第4步定義的註解@EnableDemoClient新增到Spring Applcation入口類上即可
@SpringBootApplication
@EnableDemoClient
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class,args);
}
}
實際用法示例如下:
@DemoClient
public interface Demo1Client{
String getRemoteResult(Long id);
}
@Service
public class Demo1Service{
@Autowired
private Demo1Client demo1Client;//此處實際注入的是DemoClientFactoryBean.getObject方法返回的InvocationHandler的代理類例項
public String doMany(Long id){
return demo1Client.getRemoteResult(id);//實際呼叫的是:InvocationHandler的代理類例項的invoke方法
}
}