spring boot 动态生成接口实现类
在某些业务场景中,我们只需要业务代码中定义相应的接口或者相应的注解,并不需要实现对应的逻辑。
比如 mybatis和feign: 在 mybatis 中,我们只需要定义对应的mapper接口;在 feign 中,我们只需要定义对应业务系统中的接口即可。
那么在这种场景下,具体的业务逻辑时怎么执行的呢,其实原理都是动态代理。
我们这里不具体介绍动态代理,主要看一下它在springboot项目中的实际应用,下面我们模仿feign来实现一个调用三方接口的 httpclient。
一: 定义注解
package com.mysgk.blogdemo.annotation;
/**
* @author mysgk
* @link https://www.cnblogs.com/mysgk/p/15607900.html
*/
public @interface MyHttpClient {
}
二: 建立动态代理类
package com.mysgk.blogdemo.proxy;
import org.springframework.beans.factory.FactoryBean;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author mysgk
* @link https://www.cnblogs.com/mysgk/p/15607900.html
*/
public class RibbonAopProxyFactory<T> implements FactoryBean<T>, InvocationHandler {
private Class<T> interfaceClass;
public Class<T> getInterfaceClass() {
return interfaceClass;
}
public void setInterfaceClass(Class<T> interfaceClass) {
this.interfaceClass = interfaceClass;
}
@Override
public T getObject() throws Exception {
return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{interfaceClass}, this);
}
@Override
public Class<?> getObjectType() {
return interfaceClass;
}
@Override
public boolean isSingleton() {
return true;
}
/**
真正执行的方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return "invoke " + proxy.getClass().getName() + "." + method.getName() + " , do anything ..";
}
}
三: 注入spring容器
package com.mysgk.blogdemo.start;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import com.mysgk.blogdemo.annotation.MyHttpClient;
import com.mysgk.blogdemo.proxy.RibbonAopProxyFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.Set;
/**
* @author mysgk
* @link https://www.cnblogs.com/mysgk/p/15607900.html
*/
@Component
public class ScanHttpClients implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {
private final Logger logger = LoggerFactory.getLogger(ScanHttpClients.class);
private ApplicationContext ctx;
public void run(BeanDefinitionRegistry registry) {
Set<Class<?>> scanPackage = ClassUtil.scanPackageByAnnotation("com.mysgk", MyHttpClient.class);
for (Class<?> cls : scanPackage) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(cls);
GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
definition.getPropertyValues().add("interfaceClass", definition.getBeanClassName());
definition.setBeanClass(RibbonAopProxyFactory.class);
definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
String beanName = StrUtil.removePreAndLowerFirst(cls.getSimpleName(), 0) + "RibbonClient";
registry.registerBeanDefinition(beanName, definition);
}
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
run(registry);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx = ctx;
}
}
四: 编写拦截器
package com.mysgk.blogdemo.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
/**
* @author mysgk
* @link https://www.cnblogs.com/mysgk/p/15607900.html
*/
@Component
@Aspect
public class InterceptAnnotation {
@Autowired
private RestTemplate ribbonLoadBalanced;
@Pointcut("@annotation(com.mysgk.blogdemo.annotation.MyHttpClient)")
public void execute() {
}
@Around("execute()")
public Object interceptAnnotation(ProceedingJoinPoint joinPoint) throws Throwable {
/**
* 此处省略 获取 url, httpMethod, requestEntity, responseType 等参数的处理过程
*/
ResponseEntity<?> exchange = ribbonLoadBalanced.exchange("url", HttpMethod.GET, HttpEntity.EMPTY, Object.class);
return exchange.getBody();
}
}
五: 新建测试类
package com.mysgk.blogdemo.client;
import com.mysgk.blogdemo.annotation.MyHttpClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@MyHttpClient
public interface MyHttpClientTest {
@PostMapping(value = "test/t1")
Object test(String param);
}
项目结构:

spring boot 动态生成接口实现类的更多相关文章
- spring boot 结合Redis 实现工具类
自己整理了 spring boot 结合 Redis 的工具类引入依赖 <dependency> <groupId>org.springframework.boot</g ...
- Spring Boot 之:接口参数校验
Spring Boot 之:接口参数校验,学习资料 网址 SpringBoot(八) JSR-303 数据验证(写的比较好) https://qq343509740.gitee.io/2018/07/ ...
- Spring Boot动态注入删除bean
Spring Boot动态注入删除bean 概述 因为如果采用配置文件或者注解,我们要加入对象的话,还要重启服务,如果我们想要避免这一情况就得采用动态处理bean,包括:动态注入,动态删除. 动态注入 ...
- Spring Boot@Component注解下的类无法@Autowired的问题
title: Spring Boot@Component注解下的类无法@Autowired的问题 date: 2019-06-26 08:30:03 categories: Spring Boot t ...
- Skywalking-09:OAL原理——如何通过动态生成的Class类保存数据
OAL 如何通过动态生成的 Class 类,保存数据 前置工作 OAL 如何将动态生成的 SourceDispatcher 添加到 DispatcherManager // org.apache.sk ...
- SaaS 系统架构,Spring Boot 动态数据源实现!
这段时候在准备从零开始做一套SaaS系统,之前的经验都是开发单数据库系统并没有接触过SaaS系统,所以接到这个任务的时候也有也些头疼,不过办法部比困难多,难得的机会. 在网上找了很多关于SaaS的资料 ...
- 43. Spring Boot动态数据源(多数据源自动切换)【从零开始学Spring Boot】
[视频&交流平台] àSpringBoot视频 http://study.163.com/course/introduction.htm?courseId=1004329008&utm ...
- Spring Boot 动态数据源(多数据源自己主动切换)
本文实现案例场景: 某系统除了须要从自己的主要数据库上读取和管理数据外.另一部分业务涉及到其它多个数据库,要求能够在不论什么方法上能够灵活指定详细要操作的数据库. 为了在开发中以最简单的方法使用,本文 ...
- (43). Spring Boot动态数据源(多数据源自动切换)【从零开始学Spring Boot】
在上一篇我们介绍了多数据源,但是我们会发现在实际中我们很少直接获取数据源对象进行操作,我们常用的是jdbcTemplate或者是jpa进行操作数据库.那么这一节我们将要介绍怎么进行多数据源动态切换.添 ...
随机推荐
- 题解 [PA2019]Trzy kule
link Description 对于两个长度为 \(n\) 的 \(01\) 串 \(a_1,a_2,\dots,a_n\) 和 \(b_1,b_2,\dots,b_n\),定义它们的距离 \(d( ...
- css如何简单设置文字溢出盒子显示省略号
1.单行文本溢出显示省略号单行文本溢出显示省略号,必须满足三个条件:(1)先强制一行内显示文本white-space:nowrap;(默认 normal自动换行)(2)超出的部分隐藏overflow: ...
- 微软Windows11安卓子系统已支持运行APK 应用(附手把手详细安装攻略)怎么安装安卓/如何安装安卓应用/支持多窗口多任务
10 月 21 日消息,微软博客宣称,Windows 11 上 安卓子系统运行 Android 应用程序的第一个预览版现已提供给美国 Beta 频道的 Windows 内部人员.但现在通过教程 ...
- 【c++ Prime 学习笔记】目录索引
第1章 开始 第Ⅰ部分 C++基础 第2章 变量和基本类型 第3章 字符串.向量和数组 第4章 表达式 第5章 语句 第6章 函数 第7章 类 第 Ⅱ 部分 C++标准库 第8章 IO库 第9章 顺序 ...
- pagelayout在py中的引用不支持size_hint和pos_hint
from kivy.uix.pagelayout import PageLayout from kivy.uix.button import Button from kivy.app import A ...
- Noip模拟84 2021.10.27
以后估计都是用\(markdown\)来写了,可能风格会有变化 T1 宝藏 这两天老是会的题打不对,还是要细心... 考场上打的是维护\(set\)的做法,但是是最后才想出来的,没有维护对于是没有交. ...
- Go语言核心36讲(Go语言进阶技术九)--学习笔记
15 | 关于指针的有限操作 在前面的文章中,我们已经提到过很多次"指针"了,你应该已经比较熟悉了.不过,我们那时大多指的是指针类型及其对应的指针值,今天我们讲的则是更为深入的内容 ...
- 洛谷 P5785 [SDOI2012] 任务安排
链接: P5785 弱化版:P2365 题意: 有 \(n\) 个任务待完成,每个任务有一个完成时间 \(t_i\) 和费用系数 \(f_i\),相邻的任务可以被分成一批.从零时刻开始这些任务会被机器 ...
- SpringCloud微服务实战——搭建企业级开发框架(十):使用Nacos分布式配置中心
随着业务的发展.微服务架构的升级,服务的数量.程序的配置日益增多(各种微服务.各种服务器地址.各种参数),传统的配置文件方式和数据库的方式已无法满足开发人员对配置管理的要求: 安全性:配置跟随源代码保 ...
- 平衡二叉树检查 牛客网 程序员面试金典 C++ Python
平衡二叉树检查 牛客网 程序员面试金典 C++ Python 题目描述 实现一个函数,检查二叉树是否平衡,平衡的定义如下,对于树中的任意一个结点,其两颗子树的高度差不超过1. 给定指向树根结点的指针T ...