世界上并没有完美的程序,但是我们并不因此而沮丧,因为写程序就是一个不断追求完美的过程。
无论是看源码还是手动实现,对于Feign我们的唯一目的就是弄懂其核心原理,因为Feign是一个顶级封装,并且其实现也是使用的非常规的Rxjava的Obversable,所以这里有一个常规的基于动态代理的手动实现以诠释Feign的核心原理。
首先创建注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface FeignClient {
String baseUrl() default "";
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FeignGet {
String url() default "";
}
定义使用
@Component
@FeignClient(baseUrl = "http://www.baidu.com")
public interface TestFeign {
@FeignGet(url = "/index.html")
String get();
}
控制层输出
@RestController
public void TestController {
@Autowired
private TestFeign testFeign;
@RequestMapping("/feign")
public String feign () {
return testFeign.get();
}
}
然后是核心实现,主要目的是包扫描、动态代理以及注册Bean
/**
* 通过动态代理将Feign的Bean注册到spring容器中
*/
public class FeignRegister implements
ImportBeanDefinitionRegistrar,
EnvironmentAware,
BeanClassLoaderAware,
ResourceLoaderAware,
BeanFactoryAware {
private Environment environment;
private ClassLoader classLoader;
private ResourceLoader resourceLoader;
private BeanFactory beanFactory;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
scan();
}
private void scan () {
// 构建包扫描器
ClassPathScanningCandidateComponentProvider scan =
new ClassPathScanningCandidateComponentProvider(false, environment) {
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
if (beanDefinition.getMetadata().isInterface()) {
try {
Class<?> clazz = ClassUtils.forName(
beanDefinition.getMetadata().getClassName(),
classLoader);
return !clazz.isAnnotation();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return super.isCandidateComponent(beanDefinition);
}
};
scan.setResourceLoader(resourceLoader);
AnnotationTypeFilter typeFilter = new AnnotationTypeFilter(FeignClient.class);
scan.addIncludeFilter(typeFilter);
// 扫描指定的包
String basePackage = "com.test.demo.feign";
Set<BeanDefinition> beanDefinitionSet = scan.findCandidateComponents(basePackage);
// 注册bean
beanDefinitionSet.forEach(beanDefinition -> {
try {
if (beanDefinition instanceof AnnotatedBeanDefinition) {
String className = beanDefinition.getBeanClassName();
// 创建动态代理
Class<?> target = Class.forName(className);
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
FeignClient feignClient = target.getAnnotation(FeignClient.class);
FeignGet feignGet = method.getAnnotation(FeignGet.class);
String baseUrl = feignClient.baseUrl();
String url = feignGet.url();
String allUrl = StrUtil.concat(true, baseUrl, url);
String re = new RestTemplate().getForObject(allUrl, String.class, "");
System.out.println("re : " + re);
return re;
}
};
Object proxy = Proxy.newProxyInstance(classLoader, new Class[]{target}, invocationHandler);
// 注册
((DefaultListableBeanFactory) beanFactory).registerSingleton(className, proxy);
}
} catch (Exception e) {e.printStackTrace();}
});
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
启动类中引入注册类
@SpringBootApplication
@Import(FeignRegister.class)
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
然后启动运行即可,最终结果
代码有自解释的能力,所以在这里不多做赘述。并且我认为里面有好多我们实际开发中用到的片段,比如如何进行包扫描,如何实现动态代理,如何注册自定义Bean等,希望对大家有所帮助。