spring boot 动态生成接口实现类的场景分析

目录
  • 一: 定义注解
  • 二: 建立动态代理类
  • 三: 注入spring容器
  • 四: 编写拦截器
  • 五: 新建测试类

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

比如 mybatis和feign: 在 mybatis 中,我们只需要定义对应的mapper接口;在 feign 中,我们只需要定义对应业务系统中的接口即可。

那么在这种场景下,具体的业务逻辑时怎么执行的呢,其实原理都是动态代理。

我们这里不具体介绍动态代理,主要看一下它在springboot项目中的实际应用,下面我们模仿feign来实现一个调用三方接口的 httpclient。

一: 定义注解

package com.mysgk.blogdemo.annotation;

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;

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;

@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;
@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 接口实现类内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 使用Springboot根据配置文件动态注入接口实现类

    Springboot根据配置文件动态注入接口实现类 需求 最近在做一个Springboot项目,需要面向不同需求的客户,但是为了方便管理分支,需要将不同客户的需求都写到同一套代码中,根据不同客户实例化对应的实现类. 实现 为了尽量不修改代码,少做不必要的逻辑判断,我们考虑为不同客户写不同的Service,然后根据配置参数实例化对应的Service.这样就遇到了需要根据配置文件实现不同类的需求. 针对这一需求大致有两种实现方式.但是针对我的需求,能用的只有第二种,但还是想将第一种一起总结一下. 两

  • SpringBoot同一接口多个实现类配置的实例详解

    SpringBoot项目中可能出现一个接口有多个实现类的情况,如果不进行配置,注入接口时编译器不知道要注入哪个实现类就会报错,因此需要进行配置.以下进行举例: 接口如下: public interface NoticeService { public String noticeUser(Long id); } 两个实现类如下: @Service public class NoticeServiceImpl1 implements NoticeService { public String not

  • Spring Boot中如何使用Convert接口实现类型转换器

    目录 使用Convert接口实现类型转换器 Converter接口 添加依赖 实体类 1.User类 2.Article类 配置类型转化器 1.定义全局日期转换器 2.定义全局对象转换器 3.定义全局List类型转换器 控制器 测试 Converter使用及其原理 配置文件中对Converter的引用 以字符串去空为例 我们查看Converter接口的源码 我们查看对应的成员变量: 使用Convert接口实现类型转换器 在Spring3中引入了一个Converter接口,它支持从一个Object

  • spring boot 动态生成接口实现类的场景分析

    目录 一: 定义注解 二: 建立动态代理类 三: 注入spring容器 四: 编写拦截器 五: 新建测试类 在某些业务场景中,我们只需要业务代码中定义相应的接口或者相应的注解,并不需要实现对应的逻辑. 比如 mybatis和feign: 在 mybatis 中,我们只需要定义对应的mapper接口:在 feign 中,我们只需要定义对应业务系统中的接口即可. 那么在这种场景下,具体的业务逻辑时怎么执行的呢,其实原理都是动态代理. 我们这里不具体介绍动态代理,主要看一下它在springboot项目

  • Spring Boot整合流控组件Sentinel的场景分析

    目录 一.百度百科 1.Sentinel 特性 2.Sentinel 的开源生态 二.Sentinel 的历史 三.Sentinel 基本概念 1.资源 2.规则 四.sentinel的优势 五.Sentinel 功能和设计理念 1.什么是流量控制 2.流量控制设计理念 3.什么是熔断降级 4.熔断降级设计理念 5.系统自适应保护 6.Sentinel 是如何工作的 7.竞品对比 六.SpringBoot整合sentinel 1.加入pom 2.编写sentinel规则 3.测试 七.sprin

  • Spring Boot整合Zookeeper实现分布式锁的场景分析

    目录 一.Java当中关于锁的概念 1.1.什么是锁 1.2.锁的使用场景 1.3.什么是分布式锁 1.4.分布式锁的使用场景 二.zk实现分布式锁 2.1.zk中锁的种类: 2.2.zk如何上读锁 2.3.zk如何上写锁 2.4.⽺群效应 三.springboot整合分布式锁 温馨提示:本篇文章要求掌握zk的数据结构,以及临时序号节点! zk实现分布式锁完全是依靠zk节点类型当中的临时序号节点来实现的 一.Java当中关于锁的概念 1.1.什么是锁 锁是用来控制多个线程访问共享资源的方式,一般

  • Spring Boot配置线程池拒绝策略的场景分析(妥善处理好溢出的任务)

    目录 场景重现 配置拒绝策略 代码示例 通过之前三篇关于Spring Boot异步任务实现的博文,我们分别学会了用@Async创建异步任务.为异步任务配置线程池.使用多个线程池隔离不同的异步任务.今天这篇,我们继续对上面的知识进行完善和优化! 如果你已经看过上面几篇内容并已经掌握之后,一起来思考下面这个问题: 假设,线程池配置为核心线程数2.最大线程数2.缓冲队列长度2.此时,有5个异步任务同时开始,会发生什么? 场景重现 我们先来把上面的假设用代码实现一下: 第一步:创建Spring Boot

  • 关于Spring Boot动态权限变更问题的实现方案

    1.前言 ​  在Web项目中,权限管理即权限访问控制为网站访问安全提供了保障,并且很多项目使用了Session作为缓存,结合AOP技术进行token认证和权限控制.权限控制流程大致如下图所示: ​  现在,如果管理员修改了用户的角色,或修改了角色的权限,都会导致用户权限发生变化,此时如何实现动态权限变更,使得前端能够更新用户的权限树,后端访问鉴权AOP模块能够知悉这种变更呢? 2.问题及解决方案 ​​  现在的问题是,管理员没法访问用户Session,因此没法将变更通知此用户.而用户如果已经登

  • Java Spring boot实现生成二维码

    目录 一.引入springboot依赖: 二.工具类代码: 三.调用工具类生成二维码 1.将链接生成二维码图片并保存到指定路径 2.将链接生成二维码直接显示在页面 3.将以get请求传参链接生成二维码 总结 一.引入spring boot依赖: <!--引入生成二维码的依赖--> <!-- https://mvnrepository.com/artifact/com.google.zxing/core --> <dependency> <groupId>co

  • JSP 开发之Spring Boot 动态创建Bean

    JSP 开发之Spring Boot 动态创建Bean 1.通过注解@Import导入方式创建 a.新建MyImportBeanDefinitionRegistrar注册中心 Java代码 import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.GenericBeanDefinition; import org

  • spring boot结合Redis实现工具类的方法示例

    自己整理了 spring boot 结合 Redis 的工具类 引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 加入配置 # Redis数据库索引(默认为0) spring.redis.database=0 # Redis

  • 浅谈Spring Boot 开发REST接口最佳实践

    本文介绍了Spring Boot 开发REST接口最佳实践,分享给大家,具体如下: HTTP动词与SQL命令对应 GET 从服务器获取资源,可一个或者多个,对应SQL命令中的SELECT GET /users 获取服务器上的所有的用户信息 GET /users/ID 获取指定ID的用户信息 POST 在服务器上创建一个新资源,对应SQL命令中的CREATE POST /users 创建一个新的用户 PUT 在服务器上更新一个资源,客户端提供改变后的完整资源,对应SQL命令中的UPDATE PUT

  • spring boot动态切换数据源的实现

    当数据量比较大的时候,我们就需要考虑读写分离了,也就是动态切换数据库连接,对指定的数据库进行操作.在spring中实现动态的切换无非就是利用AOP实现.我们可以使用mybatis-plus作者开发的插件dynamic-datasource-spring-boot-starter. demo地址:https://github.com/songshijun1995/spring-boot-dynamic-demo 新建项目引入依赖 <dependency> <groupId>com.b

随机推荐