OpenFeign为接口创建代理bean的方法
大约 2 分钟
实现 OpenFeign 为接口创建代理 bean 的方法
最近在写一个开发工具包,实现启动自动创建代理 bean 的方法,看了一下 OpenFeign 的源码。之前写的方法也可以实现之前的文章。 这里介绍 OpenFeign 的实现方法:
标记接口的注解类 (@FeignClient)
package cn.linkot.boot.meow.annocation;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Cat {
String value() default "";
}
启动 Import 开启创建代理 bean 注解 (@EnableFeignClients)
package cn.linkot.boot.meow;
import org.springframework.context.annotation.Import;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(CatRegistrar.class)
public @interface EnableCats {
String[] value() default {};
String[] basePackages() default {};
}
注册 BeanDefinition 类 (FeignClientsRegistrar)
package cn.linkot.boot.meow;
import cn.linkot.boot.meow.annocation.Cat;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class CatRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
private Environment environment;
private ResourceLoader resourceLoader;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(Cat.class));
for (String basePackage : getBasePackages(importingClassMetadata)) {
Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition){
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata metadata = beanDefinition.getMetadata();
Assert.isTrue(metadata.isInterface(), "Cat 注解只能用在接口上");
//获取 @Cat 注解属性
Map<String, Object> attributes = metadata.getAnnotationAttributes(Cat.class.getCanonicalName());
registryCat(registry, metadata, attributes);
}
}
}
}
/**
* 为 @Cat 接口注册 bean
* @param registry BeanDefinition 注册器
* @param metadata Cat 注解 metaData
* @param attributes Cat 注解属性
*/
private void registryCat(BeanDefinitionRegistry registry, AnnotationMetadata metadata, Map<String, Object> attributes) {
String className = metadata.getClassName();
Class clazz = ClassUtils.resolveClassName(className, null);
CatFactoryBean catFactoryBean = new CatFactoryBean(clazz);
//beanInstanceSupplier BeanDefinition 实例化提供函数
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
catFactoryBean.setName(attributes.get("value").toString());
return catFactoryBean.getObject();
});
beanDefinitionBuilder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); //类注入
beanDefinitionBuilder.setLazyInit(true); //懒加载
registry.registerBeanDefinition(className, beanDefinitionBuilder.getBeanDefinition());
}
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;
}
/**
* 通过 获取 @EnableCats 的 basePackage 属性
* @param importingClassMetadata Import的类属性
* @return basePackages | 打 @EnableCats 注解的类的包
*/
protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
Map<String, Object> attributes = importingClassMetadata
.getAnnotationAttributes(EnableCats.class.getCanonicalName());
Set<String> basePackages = new HashSet<>();
for (String pkg : (String[]) attributes.get("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (String pkg : (String[]) attributes.get("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
//如果没指定 value/basePackages
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
}
return basePackages;
}
}
创建 bean 的 factory
package cn.linkot.boot.meow;
import lombok.Getter;
import org.springframework.beans.factory.FactoryBean;
import java.lang.reflect.Proxy;
/**
* 用于创建 Cat 的 FactoryBean
*/
public class CatFactoryBean implements FactoryBean<Object> {
private final Class<?> type;
@Getter
private String name;
public CatFactoryBean(Class<?> type) {
this.type = type;
}
@Override
public Object getObject() {
return Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, new MeowHandler(name));
}
@Override
public Class<?> getObjectType() {
return type;
}
public void setName(String name) {
this.name = name;
}
}
bean 代理类
package cn.linkot.boot.meow;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* Cat 代理实现类
*/
public class MeowHandler implements InvocationHandler {
private final String name;
public MeowHandler(String name) {
this.name = name;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//防止 Object 方法 ... equals 等 触发 NULLPTR
if (method.getDeclaringClass().equals(Object.class)) return method.invoke(this, args);
System.out.println(name + ": " + method.getName());
return null;
}
}
测试
启动 Cat 注册代理 bean
package cn.linkot.boot;
import cn.linkot.boot.cat.HelloCat;
import cn.linkot.boot.meow.EnableCats;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
@EnableCats
public class BootApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(BootApplication.class, args);
HelloCat cat = ctx.getBean(HelloCat.class);
cat.sayHello();
}
}
打 @Cat 的接口
package cn.linkot.boot.cat;
import cn.linkot.boot.meow.annocation.Cat;
@Cat("HelloCat!")
public interface HelloCat {
void sayHello();
}
运行结果
console 输出:
HelloCat!: sayHello