在某些业务场景中,我们只需要业务代码中定义相应的接口或者相应的注解,并不需要实现对应的逻辑。

比如 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 动态生成接口实现类的更多相关文章

  1. spring boot 结合Redis 实现工具类

    自己整理了 spring boot 结合 Redis 的工具类引入依赖 <dependency> <groupId>org.springframework.boot</g ...

  2. Spring Boot 之:接口参数校验

    Spring Boot 之:接口参数校验,学习资料 网址 SpringBoot(八) JSR-303 数据验证(写的比较好) https://qq343509740.gitee.io/2018/07/ ...

  3. Spring Boot动态注入删除bean

    Spring Boot动态注入删除bean 概述 因为如果采用配置文件或者注解,我们要加入对象的话,还要重启服务,如果我们想要避免这一情况就得采用动态处理bean,包括:动态注入,动态删除. 动态注入 ...

  4. Spring Boot@Component注解下的类无法@Autowired的问题

    title: Spring Boot@Component注解下的类无法@Autowired的问题 date: 2019-06-26 08:30:03 categories: Spring Boot t ...

  5. Skywalking-09:OAL原理——如何通过动态生成的Class类保存数据

    OAL 如何通过动态生成的 Class 类,保存数据 前置工作 OAL 如何将动态生成的 SourceDispatcher 添加到 DispatcherManager // org.apache.sk ...

  6. SaaS 系统架构,Spring Boot 动态数据源实现!

    这段时候在准备从零开始做一套SaaS系统,之前的经验都是开发单数据库系统并没有接触过SaaS系统,所以接到这个任务的时候也有也些头疼,不过办法部比困难多,难得的机会. 在网上找了很多关于SaaS的资料 ...

  7. 43. Spring Boot动态数据源(多数据源自动切换)【从零开始学Spring Boot】

    [视频&交流平台] àSpringBoot视频 http://study.163.com/course/introduction.htm?courseId=1004329008&utm ...

  8. Spring Boot 动态数据源(多数据源自己主动切换)

    本文实现案例场景: 某系统除了须要从自己的主要数据库上读取和管理数据外.另一部分业务涉及到其它多个数据库,要求能够在不论什么方法上能够灵活指定详细要操作的数据库. 为了在开发中以最简单的方法使用,本文 ...

  9. (43). Spring Boot动态数据源(多数据源自动切换)【从零开始学Spring Boot】

    在上一篇我们介绍了多数据源,但是我们会发现在实际中我们很少直接获取数据源对象进行操作,我们常用的是jdbcTemplate或者是jpa进行操作数据库.那么这一节我们将要介绍怎么进行多数据源动态切换.添 ...

随机推荐

  1. 运行WampServer提示计算机中丢失 msvcr110.dll

    ​ 在第一次运行WampServer的时候,出现"无法启动此程序,因为计算机中丢失 MSVCR110.dll.尝试重新安装该程序以解决此问题. ​ 在浏览器的地址栏里输入 http://ww ...

  2. modal框

    modal框 创建modal款的基本"框架": 1 <body> 2 <!--1.触发模态框的按钮--> 3 <button class=" ...

  3. SudokuSolver 1.0:用C++实现的数独解题程序 【二】

    本篇是 SudokuSolver 1.0:用C++实现的数独解题程序 [一] 的续篇. CQuizDealer::loadQuiz 接口实现 1 CQuizDealer* CQuizDealer::s ...

  4. 初探webpack之从零搭建Vue开发环境

    初探webpack之搭建Vue开发环境 平时我们可以用vue-cli很方便地搭建Vue的开发环境,vue-cli确实是个好东西,让我们不需要关心webpack等一些繁杂的配置,然后直接开始写业务代码, ...

  5. NOIP模拟79

    T1 F 解题思路 因为每个点会产生贡献当且仅当它在可以到他的点之前被删除,并且此题遵守期望的线性性. 因此设所有可以到达点 \(i\) 的数量为 \(c_i\) 那么答案就是 \(\sum \fra ...

  6. 内网渗透DC-2靶场通关(CTF)

    为了更好的阅读体验,请在pc端打开我的个人博客 DC系列共9个靶场,本次来试玩一下DC-2,共有5个flag,下载地址. 下载下来后是 .ova 格式,建议使用vitualbox进行搭建,vmware ...

  7. WIFI Deauth攻击-爬坑笔记

    这里用Aircrack这款工具进行介绍: 准备工作:无线网卡连接电脑或者虚拟机(免驱的最好),如需驱动请自行安装驱动 1.将无线网卡接入测试电脑Linux虚拟机(装有Aircrack-ng) 2.测试 ...

  8. 【数据结构】<栈的应用>回文判断

    通过栈与队列相关内容的学习,我们知道,栈是"先进后出"的线性表,而队列是"先进先出"的线性表.可以通过构造栈与队列来实现在这一算法.将要判断的字符序列依次压栈和 ...

  9. Framework - 性能统计

    摘要 近期对接客户时,客户方希望提供 SDK 的性能.内存.隐私支持等一些数据,所以就对 SDK 进行了一些性能测试. 在用表格统计整理这些数据时,突然发现,经常用统计的方式看 SDK 的相关数据,似 ...

  10. Django Model字段加密的优雅实现

    早前的一篇文章Django开发密码管理表实例有写我们写了个密码管理工具来实现对密码的管理,当时加密解密的功能在view层实现,一直运行稳定所以也没有过多关注实现是否优雅的问题.最近要多加几个密码表再次 ...