使用FactoryBean和JDK代理实现动态注册接口Bean
一、介绍
本文将介绍如何通过FactoryBean和JDK动态代理实现动态注册接口Bean,做到无具体实现的类也能调用方法,类似openFeign中的接口调用和mybatis中的Mapper,下面将使用openFeign的示例讲解实现过程。
二、步骤
-
创建注解类
EnableFeignClients.java
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(FeignClientsRegistrar.class) public @interface EnableFeignClients { String[] basePackages() default {}; }
FeignClient.java
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented public @interface FeignClient { String value(); }
测试接口类RemoteUserService.java
@FeignClient(value = "user-server") public interface RemoteUserService { @GetMapping("/getUser") Object getUser(@RequestParam String id); }
-
创建FeignClientFactoryBean.java,通过jdk动态代理获取代理对象
public class FeignClientFactoryBean implements FactoryBean<Object> { private Class<?> type; public FeignClientFactoryBean(Class<?> type) { this.type = type; } @Override public Object getObject() { return this.getTarget(); } /** * 通过动态代理获取代理对象(重要) * @param <T> * @return */ <T> T getTarget() { return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, new FeignProxy()); } @Override public Class<?> getObjectType() { return type; } }
-
创建代理类FeignProxy.java,具体的方法执行都在这个类
public class FeignProxy implements InvocationHandler { private static final Logger log = LoggerFactory.getLogger(FeignProxy.class); @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log.info("类{},执行了{}方法,参数={}", method.getDeclaringClass().getName(), method.getName(),args); return "张三"; } }
-
创建注册所有的FeignClient的类FeignClientsRegistrar.java
伪代码:
- 获取所有的包路径,从EnableFeignClients注解上的属性以及启动类所在的包获得
- 通过ClassPathScanningCandidateComponentProvider类扫码所有带FeignClient注解的类
- 遍历所有获取到带FeignClient类并进行通过BeanDefinitionRegistry进行bean注册
- 注册这类的bean是通过FactoryBean去创建对象
@Configuration public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware { private ResourceLoader resourceLoader; private Environment environment; @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet(); Set<String> basePackages = getBasePackage(metadata); for (String basePackage : basePackages) { ClassPathScanningCandidateComponentProvider scanner = this.createScanner(); scanner.setResourceLoader(this.resourceLoader); scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class)); // 添加所有扫码到的类 candidateComponents.addAll(scanner.findCandidateComponents(basePackage)); } for (BeanDefinition beanDefinition : candidateComponents) { if (beanDefinition instanceof AnnotatedBeanDefinition) { AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition; AnnotationMetadata annotationMetadata = annotatedBeanDefinition.getMetadata(); // 注册bean registerFeignClient(registry, annotationMetadata); } } } /** * 注册bean * * @param registry * @param annotationMetadata */ private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata) { // 获取class名称 String className = annotationMetadata.getClassName(); Class<?> clazz = null; try { clazz = Class.forName(className); } catch (ClassNotFoundException e) { e.printStackTrace(); } FeignClientFactoryBean factoryBean = new FeignClientFactoryBean(clazz); BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, new Supplier() { @Override public Object get() { return factoryBean.getObject(); } }); registry.registerBeanDefinition(clazz.getName(), definition.getBeanDefinition()); } /** * 创建类路径扫描器 * * @return */ private ClassPathScanningCandidateComponentProvider createScanner() { return new ClassPathScanningCandidateComponentProvider(false, this.environment) { protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { boolean isCandidate = false; // isIndependent是顶级接口,isAnnotation是注解类 if (beanDefinition.getMetadata().isIndependent() && !beanDefinition.getMetadata().isAnnotation()) { isCandidate = true; } return isCandidate; } }; } /** * 获取 * * @param metadata * @return */ private Set<String> getBasePackage(AnnotationMetadata metadata) { Set<String> basePackage = new HashSet<>(); // 获取启动类的包路径 String bootstrapClassName = metadata.getClassName(); String bootstrapClassPackage = bootstrapClassName.substring(0, bootstrapClassName.lastIndexOf(".")); basePackage.add(bootstrapClassPackage); // 获取注解上面的包路径 Map<String, Object> attributes = metadata.getAnnotationAttributes(EnableFeignClients.class.getCanonicalName()); String[] basePackages = (String[]) attributes.get("basePackages"); basePackage.addAll(Arrays.asList(basePackages)); return basePackage; } @Override public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } }
三、实现效果
通过下图发现RemoteUserService类是可以正常注入,执行getUser方法真正会执行到FeignProxy.java,同时可以获取到源类、方法、参数。
四、源码
https://github.com/1277463718lmt/interface-bean-registry-demo.git