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

比如 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. 容器化之路Docker网络核心知识小结,理清楚了吗?

    Docker网络是容器化中最难理解的一点也是整个容器化中最容易出问题又难以排查的地方,加上使用Kubernets后大部分人即使是专业运维如果没有扎实的网络知识也很难定位容器网络问题,因此这里就容器网络 ...

  2. I/O系统

    I/O系统的组成 外部设备 接口部件 总线 相应的管理软件 I/O软件 将用户编制的程序(或数据)输入主机内 将运算结果输出给用户 实现输入输出系统与主机工作的协调 I/O系统的基本功能 完成计算机内 ...

  3. 【数据结构】c语言实现集合的交并差运算

    待改写:存储数据类型int-->char 重复的元素可存储 功能上不完善 #include <stdio.h> #include <stdlib.h> typedef s ...

  4. 力扣 - 剑指 Offer 45. 把数组排成最小的数

    题目 剑指 Offer 45. 把数组排成最小的数 思路1 将整数数组转化成字符串数组 然后使用Arrays工具类的sort方法帮助我们排序 代码 class Solution { public St ...

  5. Scrum Meeting 16

    第16次例会报告 日期:2021年06月11日 会议主要内容概述: 最后一次例会 一.进度情况 我们采用日报的形式记录每个人的具体进度,链接Home · Wiki,如下记录仅为保证公开性: 组员 负责 ...

  6. AIApe问答机器人Scrum Meeting 4.25

    Scrum Meeting 2 日期:2021年4月25日 会议主要内容概述:前后端针对WebAPI进行协调与统一工作,商量接下来两日计划:敲定部分设计细节. 一.进度情况 组员 负责 两日内已完成的 ...

  7. 第四单元博客总结——暨OO课程总结

    第四单元博客总结--暨OO课程总结 第四单元架构设计 第一次UML作业 简单陈述 第一次作业较为简单,只需要实现查询功能,并在查询的同时考虑到性能问题,即我简单的将每一次查询的结果以及递归的上层结果都 ...

  8. Python爬取COVID-19疫情监控实战

    一.项目概述 本项目基于Python.Flask.Echarts打造的一个疫情监控系统,涉及技术: Python网络爬虫 Python与Mysql数据库交互 使用Flask构建web项目 基于Echa ...

  9. tarjan知识点梳理

    tarjan在图论中还是挺重要的.这里就简要的梳理一下tarjan的知识点. tarjan算法与无向图连通性. 首先说一下图中割点和桥的定义. 桥:也称割边,定义类似,在无向图中,若去掉某条边,导致整 ...

  10. Oracle日志 归档模式管理

    一.查看当前归档模式 archive log list; 二.归档/不归档切换 shutdown immediate; -- 或shutdown normal startup mount; alter ...