Spring工程通过注解实现动态声明Bean

此前在一个旧的spring项目中实现了手动配置接入Eureka,同时生成了一个Feign的客户端。刚好现在有一个新的微服务API需要接入。于是想实现在springboot中通过@FeignClient自动声明客户端bean的功能,过程也不是很复杂,在此分享一下过程

一、实现@FeignClient注解

@FeignClient是 spring-cloud-starter-openfeign 项目中用于springboot自动配置声明Feign客户端的注解,并不属于Feign原有的注解,所以这里我要自己去写一个@FeignClient自己用

1@Target(value = ElementType.TYPE) 2@Retention(value = RetentionPolicy.RUNTIME) 3@Documented 4public @interface FeignClient{ 5 String value(); 6}

由于构造客户端时我只需要用到一个url,所以不需要额外的属性,有一个value足矣。有其他需要的朋友可以自己添加需要的注解属性。下面是一个Feign客户端,使用了自定义的FeignClient注解

1package my.feign.client; 2 3import my.annotation.FeignClient; 4import my.app.model.* 5 6import feign.Headers; 7import feign.RequestLine; 8 9@FeignClient("http://unionpay-api") 10@Headers({ "Content-Type: application/json", "Accept: application/json;charset=UTF-8" }) 11public interface UnionpayService { 12 @RequestLine("POST /pos/transaction/pay") 13 PayResult pay(PayRequest request); 14}

二、动态注册Bean

工程启动时我需要扫描指定包内的代码,查找所有@FeignClient注解的类,数量也是不定的,所以这里我们需要动态的声明bean方式。关键就是 BeanDefinitionRegistryPostProcessor 这个spring接口。这个接口扩展自BeanFactoryPostProcessor,专门用于动态注册Bean,spring官方解释是:允许在正常的BeanFactoryPostProcessor检测开始_之前_注册更多的自定义bean。

在这里我只利用它优先于其他Bean声明检测前执行这一特性,否则在其他类使用@Autowire注入我们的客户端时会报异常。

本例我们使用BeanFactory来注册一个单例Bean,所以使用postProcessBeanFactory方法

1@Configuration 2public class FeignConfig implements BeanDefinitionRegistryPostProcessor { 3 4 // 类中还有若干方法用于构建EurekaClient和RibbonClient,由于本文主题不是如何构建Feign客户端,在此不一一列出,有需要的可以邮件找我要 mailto: kit.li@qq.com 5 6 @Override 7 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { 8 9 } 10 11 @Override 12 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 13 ApplicationInfoManager applicationInfoManager = this.applicationInfoManager(); 14 Provider<EurekaClient> provider = () -> this.eurekaClient(applicationInfoManager); 15 ServerListUpdater serverListUpdater = new EurekaNotificationServerListUpdater(provider); 16 17 // 扫描指定package下所有带有FeignClient注解的类,自已实现的方法,找我或百度都有 18 Set<Class> classSet = ClassUtil.getClass4Annotation("my.feign.client", FeignClient.class); 19 classSet.forEach(cls -> { 20 // 读取注解的value,得到服务的url,截取其中的VipAddress 21 FeignClient anno = (FeignClient) cls.getAnnotation(FeignClient.class); 22 String url = anno.value(); 23 String vip = url.substring(url.indexOf("://") + 3); 24 25 // 创建Feign客户端实例 26 IClientConfig clientConfig = this.ribbonClientConfig(cls.getName(),vip); 27 ServerList<Server> serverList = (ServerList<Server>) this.ribbonServerList(clientConfig, provider); 28 ServerListFilter<Server> serverListFilter = this.ribbonServerListFilter(clientConfig); 29 ILoadBalancer loadBalancer = new DynamicServerListLoadBalancer<>(clientConfig, new RandomRule(), 30 new NIWSDiscoveryPing(), serverList, serverListFilter, serverListUpdater); 31 Client client = RibbonClient.builder().lbClientFactory(name -> 32 LBClient.create(loadBalancer, ClientFactory.getNamedConfig(name))).build(); 33 Object service = buildServiceClient(cls, client, url); 34 35 // 注册成单例 36 beanFactory.registerSingleton(cls.getName(), service); 37 }); 38 } 39} 40

三、结语

至此已经成功实现了Bean的动态生成,适用于一些想通过注解动态声明bean的场合。例子中因为需要需要其他方式创建bean实例,所以用了postProcessBeanFactory方法。也如以使用BeanDefinition方式注册bean。例如:

1Class<?> cls = MyService.class; 2BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(cls); 3GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition(); 4definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE); 5definition.getPropertyValues().add("name","unionpay"); 6 7registry.registerBeanDefinition("myService", definition);
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

spring注解

随着越来越多地使用Springboot敏捷开发,更多地使用注解配置Spring,而不是Spring的applicationContext.xml文件。Configuration注解:Spring解析为配置类,相当于spring配置文件Bean注解:容器注册Bean组件,默认id为方法名@Configurat