背景
在日常开发中,经常用到openfeign,于是,就是想试试参考openfeign,实现一个简单版的feign.
思路
创建注解:熟悉使用OpenFeign的都知道,其有两个重要的注解,@EnableFeignClients和@FeignClient,其中@FeignClient的作用,就是声明一个Feign客户端(可以把他看成一个Controller),而@EnableFeignClients的作用就是开启Feign功能(将被@FeignClient标记的bean注入到Spring容器当中),所以,第一步,要创建两个类似的注解。
实现@EnableFeignClients的功能:我们都知道@EnableFeignClients通过basePackages或者basePackageClasses属性可以扫码某些包下的类进行扫描并注册为bean。而在SpringBoot中,提供ImportBeanDefinitionRegistrar接口和ClassPathBeanDefinitionScanner来实现类似的功能。
实现@FeignClient的功能:@FeignClient主要功能是实现接口请求,我们可以通过Spring的FactoryBean创建一个bean,在getObject()返回一个可以发起请求的代理对象。
创建EnableMyFeignClient和MyFeignClient注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MyFeignClientsRegistrar.class)
public @interface EnableMyFeignClient {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface MyFeignClient {
String name() default "";
String path() default "";
String url() default "";
String contextId() default "";
}
实现EnableMyFeignClient的功能
/**
* 获取EnableMyFeignClient的属性值,并扫描相应的包路径,注册FeignClient的BeanDefinition
* @Date 2024/11/29
*/
public class MyFeignClientsRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(EnableMyFeignClient.class.getName())
AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(attributes)
List<String> basePackages = new ArrayList<>()
String[] values = annotationAttributes.getStringArray("value")
if (values != null && values.length > 0) {
basePackages.addAll(Arrays.asList(values))
}
if (basePackages == null || basePackages.size() == 0) {
String[] strings = (String[]) attributes.get("basePackages")
basePackages.addAll(Arrays.asList(strings))
}
if (basePackages == null || basePackages.size() == 0) {
Class<?>[] basePackageClasses = (Class<?>[]) attributes.get("basePackageClasses")
for (int i = 0
String name = basePackageClasses[i].getPackage().getName()
basePackages.add(name)
}
}
if (Objects.isNull(basePackages) || basePackages.size() == 0) {
throw new IllegalArgumentException("EnableMyFeign basePackages must not be empty")
}
FeignClientClassPathBeanDefinitionScanner scanner = new FeignClientClassPathBeanDefinitionScanner(registry)
scanner.doScan(basePackages.toArray(new String[0]))
}
}
public class FeignClientClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
public FeignClientClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
super(registry);
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
addIncludeFilter(new AnnotationTypeFilter(MyFeignClient.class));
Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
for (BeanDefinitionHolder holder : beanDefinitionHolders) {
BeanDefinition beanDefinition = holder.getBeanDefinition();
GenericBeanDefinition definition = (GenericBeanDefinition) beanDefinition;
String beanClassName = definition.getBeanClassName();
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
definition.setBeanClass(MyFeignClientFactoryBean.class);
definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
return beanDefinitionHolders;
}
}
实现MyFeignClient功能
public class MyFeignClientFactoryBean<T> implements FactoryBean<T> {
private Class<T> type;
public MyFeignClientFactoryBean(Class<T> type) {
this.type = type;
}
@Override
public T getObject() throws Exception {
return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type},
new DefaultFeignClient<>(type));
}
@Override
public Class<?> getObjectType() {
return type;
}
}
/**
* 实现InvocationHandler 接口,重写invoke方法,从而实现远程请求
* @Date 2024/11/29
*/
public class DefaultFeignClient<T> implements InvocationHandler {
private Class<T> type
public DefaultFeignClient(Class<T> type) {
this.type = type
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Object 方法,走原生方法, 比如 hashCode()
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args)
}
// 其它走动态代理
Class<?> declaringClass = method.getDeclaringClass()
MyFeignClient client = declaringClass.getAnnotation(MyFeignClient.class)
//获取域名/服务ip地址
String domain = client.path()
Annotation[] annotations = method.getAnnotations()
for (Annotation annotation : annotations) {
if (annotation instanceof GetMapping) {
GetMapping getAnno = (GetMapping) annotation
//OkHttp 发起请求, getAnno.value()为GetMapping的路径
Response response = OkHttpUtils.doGet(domain + getAnno.value())
String resp = response.body().string()
return resp
}
}
return null
}
}
测试
@MyFeignClient(name = "test", path = "http://localhost:8080")
public interface TestClient {
@GetMapping("/user/get")
String test();
}
@RestController
@RequiredArgsConstructor
@RequestMapping("/order")
public class OrderController {
private final TestClient testClient;
@GetMapping(value = "/test")
public String test() {
return testClient.test();
}
@RestController
@RequiredArgsConstructor
@Slf4j
@RequestMapping("/user")
public class UserController {
@GetMapping("/get")
public R getUserByPhone() {
return R.success("请求成功");
}
}
